# 문제
# 코드
import sys
def print2D(arrs) :
print("--")
for i in arrs :
print(*i)
DIR_UP = 1
DIR_DOWN = 2
DIR_RIGHT = 3
DIR_LEFT = 4
STATE_LIVE = 1
STATE_DIE = 0
dirs = [
(0,0),(-1,0),(1,0),(0,1),(0,-1)
]
sharks = list()
R,C,M = map(int,sys.stdin.readline().split())
board = [ [-1 for i in range(C)] for j in range(R)]
class Shark() :
def __init__(self,row,col,speed,dir,size):
self.row = row
self.col = col
self.speed = speed
self.dir = dir
self.size = size
self.life = STATE_LIVE
def setLocation(self,row,col,d):
self.row = row
self.col = col
self.dir = d
def getLocation(self):
return (self.row,self.col)
def kiiShark(self):
self.life = STATE_DIE
self.row = -1
self.col = -1
self.dir = -1
def move(self):
moveCount = self.speed
curRow = self.row; curCol = self.col
while moveCount != 0 :
dirRow, dirCol = dirs[self.dir]
nextRow = curRow + dirRow
nextCol = curCol + dirCol
if 0 <= nextRow < R and 0 <= nextCol < C :
curRow = nextRow
curCol = nextCol
moveCount -=1
else :
# 범위를 벗어나는 경우
# 방향 변경해주고 moveCount를 차감하지 않음으로써 다음번에 계산 가능하도록 함
if self.dir == DIR_UP :
self.dir = DIR_DOWN
elif self.dir == DIR_DOWN :
self.dir = DIR_UP
elif self.dir == DIR_LEFT :
self.dir = DIR_RIGHT
elif self.dir == DIR_RIGHT :
self.dir = DIR_LEFT
self.row = curRow
self.col = curCol
return (self.row,self.col,self.dir)
def __str__(self):
return f"(speed : {self.speed} size : {self.size} ) "
def isSharkLive(self):
if self.life == STATE_LIVE :
return True
else :
return False
for id in range(M) :
#(r,c), s=속력, d=이동방향, z=크기
r,c,s,d,z = map(int,sys.stdin.readline().split())
# 문제는 1행1열부터 시작하기 때문이 값에 대한 보정이 이루어져야 한다.
r -=1; c-=1;
shark = Shark(r,c,s,d,z)
sharks.append(shark)
board[r][c] = id
totalSize = 0
# 낚시왕이 한칸씩 이동하면서 맨오른쪽 칸에 도착할때까지 진행한다.
# 즉 열의 개수만큼 이동해야 한다.
for masterCol in range(C) :
# 현재 위치에서 땅과 제일 가까운 상어를 잡는다.
for searchRow in range(R) :
if board[searchRow][masterCol] != -1 :
# -1이 아니라면 그 위치에는 상어가 존재한다.
sharkId = board[searchRow][masterCol]
targetShark = sharks[sharkId]
# 잡은 상어의 크기를 더해준다.
totalSize += targetShark.size
# 해당 위치에 상어 표기를 없앤다.
board[searchRow][masterCol] = -1
# 상어를 죽여준다.
targetShark.kiiShark()
break
# 현재 살아있는 상어들로 하여금 위치 이동을 시켜줘야 한다.
afterBoard = [ [-1 for i in range(C)] for j in range(R)]
for id,shark in enumerate(sharks) :
if shark.isSharkLive() :
# 현재 좌표를 지워줄 필요가 없다. 결과는 새로운 afterboard로 이동할거니까
# 이동 시킨다. move를 수행하면서 shark의 위치 및 dir은 알아서 바꿔진다.
movedRow,movedCol,movedDir = shark.move()
# 이동 후 좌표로 업데이트 하기전에 해당 좌표의 상어와 크기 비교를 해서 업데이트 해야한다.
if afterBoard[movedRow][movedCol] != -1 :
beforeShark = sharks[afterBoard[movedRow][movedCol]]
if beforeShark.isSharkLive() :
# 현재 상어가 더 크다면, 이전상어를 죽이고, board를 현재상어의 값으로 최신화 한다.
if shark.size > beforeShark.size :
afterBoard[movedRow][movedCol] = id
beforeShark.kiiShark()
# print("beforeShark was killed! : " + str(beforeShark) + "killed by " + str(shark))
# 기존에 존재하던 상어가 더 크다면 현재 상어를 죽인다.
if shark.size < beforeShark.size :
shark.kiiShark()
# print("curShark was killed! : " + str(shark) + "killed by " + str(beforeShark) )
else :
# 이동 위치에 상어가 없을때는 그냥 이동시켜주자.
afterBoard[movedRow][movedCol] = id
# 업데이트된 보드를 현재보드로 바꿔주자
board = afterBoard
# print2D(board)
# print("totalSize : " , totalSize, "mastCol : ", masterCol)
# print()
print(totalSize)
# 풀이
이번 문제는 코드양을 줄여볼까 하고 class를 활용해서 풀어봤다. 이전까지는 파이썬에서 문제 풀때는 한번도 class만들어서 푼 적이 없었기 때문에 도전해봤다.
class Shark() :
def __init__(self,row,col,speed,dir,size):
self.row = row
self.col = col
self.speed = speed
self.dir = dir
self.size = size
self.life = STATE_LIVE
def setLocation(self,row,col,d):
pass
def getLocation(self):
pass
def kiiShark(self):
pass
def move(self):
pass
def __str__(self):
pass
def isSharkLive(self):
pass
- 이렇게 Shark객체를 만들고 문제를 풀었다. 근데 문제를 풀기전에 setLoaction이랑 getLocation을 만들어 놨었는데 결국 쓰이지는 않았다. 그러나 코드를 리팩토링할것도 아니니까 빠르게 빠르게 대충 만들고 푸니까 생각을 빠르게 구현할 수 있어서 좋았다.
- 여튼 이렇게 문제 읽으면서 필요할거 같은 기능들 대충 만들고, 바로 비즈니스 로직을 작성했다. 난 알고리즘 풀때 주석을 달면서 푸는데, 저렇게 객체를 만들고 나서 주석만 쓰는게 아니라 함수로 정의된 내용을 일단 쓰고 안에서 후에 행해져야하는 로직을 짜니까 좋았다. 이제 종종 이렇게 문제를 풀어야 겠다고 생각했다.
- 문제는 딱히 어려운건 없었다. 난 board를 만들고 거기 안에 shark의 id값을 적어두고 배열에서 이를 인덱스로 접근가능하도록 했다.. shark는 딕셔너리를 쓰지 않고 배열에 담아뒀는데 그 이유는, 후에 상어들을 board를 보고 순회하는 것이 아닌 상어들로만 빠르게 순회하고 싶기떄문에 배열에 담아 뒀다.
- 배열이고 id값은 사실 인덱스이기 때문에, 인덱싱으로 배열에 접근했다. 그렇기 때문에 배열에서 원소를 지우면 id값이 변해버리는 불상사가 벌어지기 때문에 배열에서 원소를 삭제하지는 않았다. 대신에 상어의 객체의 life를 통해서 살아있는지 죽어있는지 판단해서, 죽었을때는 순회하지 않도록 했다.
- 이러한 문제를 풀때 가장 주의해야할 점은 모든 상어들의 위치가 동시에 한칸씩 이동해야 한다는 점이다. 다른 문제 풀때도 동일한 문제로 몇번 고민했던 적이 있다. 배열을 통해서 순회하면 첫번째 상어가 끝까지 이동 후, 그 다음 상어가 이동하고, ... 이렇게 반복된다. 하지만 현실세계(구현)해야하는 목표는 모든 상어가 한칸씩 동시에 이동한다. 난 예전에는 이러한 문제를 candidate배열을 만들고 그안에 최종 목적지를 만들고 board에 업데이트 했는데, 이번에는 그냥 새로운 board만들고 거기에다가 업데이트 시켰다. 논리적으로 생각하기에는 이게 더 좋은거 같다. 정리하자면 새로운 도화지에 이동시킨다.
'•알고리즘(Algorithm ) > 문제풀이' 카테고리의 다른 글
[백준17837&파이썬] deque는 신이다. reverse, extend 활용하여 유사 링크드리스트처럼 사용하자. (1) | 2023.08.31 |
---|---|
[백준17822&파이썬] 원판을 돌릴때는 deque와 원형큐를 연상해보자 (0) | 2023.08.22 |
[백준14890&파이썬] 여러 실패케이스를 한번에 처리할 수 있는지 고민해보자 (0) | 2023.06.25 |
[백준15683&파이썬] 여러경우의 4방향 탐색 및 회전 가지치기와 완전탐색 (0) | 2023.06.23 |
[백준14891&파이썬] 톱니바퀴를 돌릴때는 deque를 사용하자 (0) | 2023.06.17 |