(BOJ)-1937, Python
(BOJ)-1937, Python
문제 요약
출처: (BOJ)1937 - 욕심쟁이 판다
$N \times N$ 크기의 대나무 숲이 있고, 각 칸마다 대나무의 양이 주어진다.
특정 칸에 판다를 풀어놓았을 때, 이 판다는 그 칸의 대나무를 모조리 다 먹고, 인접한 칸들 중 방금 먹었던 대나무 양보다 많은 곳만 골라서 먹으러 이동한다고 한다. 이때, 어느 칸에 판다를 풀어 놓아야 판다가 최대한 많은 칸을 방문할 수 있을지를 구하는 문제이다.
- 판다가 처음 풀어질 곳은 아무도 모른다.
- 현재 지점으로부터 상, 하, 좌, 우로만 움직일 수 있다.
- 판다가 처음 풀어진 곳도 카운트 해야 한다.
접근 과정
1. 완전탐색 접근(재귀)
상, 하, 좌, 우 방향으로 이동하는 경우의 수를 쪼개어 처리할 수 있기 때문에, 재귀를 활용하여 접근하기로 했다. 하지만 결과는 예상대로 시간 초과였다.
코드 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import sys
sys.setrecursionlimit(400000)
N = int(input())
graph = [list(map(int, input().split())) for _ in range(N)]
answer = 0
def recursion(y, x, count):
global answer
for mv_y, mv_x in [[-1, 0], [1, 0], [0, -1], [0, 1]]:
dy, dx = y + mv_y, x + mv_x
if 0 <= dy < N and 0 <= dx < N:
if graph[dy][dx] > graph[y][x]:
recursion(dy, dx, count + 1)
answer = max(answer, count)
for init_y in range(N):
for init_x in range(N):
recursion(init_y, init_x, 1)
print(answer)
2. DP 접근(메모이제이션)
이번 문제도 역시 재귀 코드를 바탕으로 DP(메모이제이션) 으로 접근하였고, 결과는 맞았습니다!
코드 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import sys
sys.setrecursionlimit(400000)
N = int(input())
graph = [list(map(int, input().split())) for _ in range(N)]
dp = [[0 for _ in range(N)] for _ in range(N)]
def recursion(y, x):
if dp[y][x] != 0:
return dp[y][x]
for mv_y, mv_x in [[-1, 0], [1, 0], [0, -1], [0, 1]]:
dy, dx = y + mv_y, x + mv_x
if 0 <= dy < N and 0 <= dx < N:
if graph[dy][dx] > graph[y][x]:
dp[y][x] = max(dp[y][x], recursion(dy, dx) + 1)
return dp[y][x]
for init_y in range(N):
for init_x in range(N):
recursion(init_y, init_x)
print(max(map(max, dp))+1)
3. DP 접근(점화식)
점화식으로 접근하려 했지만, 논리적인 이슈가 발생했다. 현재 위치보다 더 큰 대나무 양을 가진 칸으로만 이동해야 하는 조건 때문에, DP 테이블이 올바르게 업데이트되지 않았다.
예를 들어, 특정 줄에 4 3 2 1
로 대나무의 양이 분포되어 있다고 가정해보자.
- 올바른 DP 테이블 값은
[3, 2, 1, 0]
이 되어야 하지만, 시도한 코드에서는 $(0,0) → (N-1,N-1)$으로 점화식을 전개하다 보니 모든 값이[1, 1, 1, 1]
로 업데이트되는 문제가 발생했다.
이 문제를 해결하기 위해 고민하던 중, 어차피 각 좌표를 한 번씩만 방문하며 주변을 확인하는 방식이라면, 대나무 양을 기준으로 정렬한 후 그 순서대로 좌표를 확인하면 되지 않을까? 라는 생각을 해봤다. 이를 바탕으로 코드를 추가했으며, 결과는 맞았습니다!
시도한 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#시도한 코드
N = int(input())
graph = [list(map(int, input().split())) for _ in range(N)]
dp = [[0 for _ in range(N)] for _ in range(N)]
for y in range(N):
for x in range(N):
for mv_y, mv_x in [[-1, 0], [1, 0], [0, -1], [0, 1]]:
dy, dx = y + mv_y, x + mv_x
if 0 <= dy < N and 0 <= dx < N:
if graph[dy][dx] > graph[y][x]:
dp[dy][dx] = max(dp[y][x] + 1, dp[dy][dx])
print(dp)
코드 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
N = int(input())
graph = [list(map(int, input().split())) for _ in range(N)]
dp = [[0 for _ in range(N)] for _ in range(N)]
#조건을 바탕으로 전개하기 위해(무조건 큰 값으로 이동해야 하니)
for_BUDP = [[graph[y][x], y, x] for x in range(N) for y in range(N)]
for_BUDP.sort(key= lambda x: x[0])
for daenamoo, y, x in for_BUDP: #daenamoo는 정렬을 위해 세팅했기에 사용 x
for mv_y, mv_x in [[-1, 0], [1, 0], [0, -1], [0, 1]]:
dy, dx = y + mv_y, x + mv_x
if 0 <= dy < N and 0 <= dx < N:
if graph[dy][dx] > graph[y][x]:
dp[dy][dx] = max(dp[y][x] + 1, dp[dy][dx])
print(max(map(max,dp))+1)
회고
점화식 접근에서 조건을 해결하기 위해 시간을 많이 보낸 것 같다. 그래도 이렇게 여러 가지 방법으로 문제를 풀어보면서 힘들지만 많은 생각도 할 수 있고, 나쁘지 않은 경험인 것 같다.. 앞으로도 꾸준히 이런 연습을 해봐야겠다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.