JUMPGAME https://algospot.com/judge/problem/read/JUMPGAME
algospot.com :: JUMPGAME
외발 뛰기 문제 정보 문제 땅따먹기를 하다 질린 재하와 영훈이는 땅따먹기의 변종인 새로운 게임을 하기로 했습니다. 이 게임은 그림과 같이 n*n 크기의 격자에 각 1부터 9 사이의 정수를 쓴 상태로 시작합니다. 각 차례인 사람은 맨 왼쪽 윗 칸에서 시작해 외발로 뛰어서 오른쪽 아래 칸으로 내려가야 합니다. 이 때 각 칸에 적혀 있는 숫자만큼 오른쪽이나 아래 칸으로 움직일 수 있으며, 중간에 게임판 밖으로 벗어나면 안 됩니다. 균형을 잃어서 다른 발로 서거
algospot.com
JUMPGAME 문제는 N*N의 보드가 주어지고, 왼쪽 제일 위 부분부터 시작하여 오른쪽 제일 아래 부분까지
해당 좌표의 숫자 만큼 아래나 오른쪽으로 이동하여 도착할 수 있는지 확인하는 문제이다.
(a) 에서는 끝 까지 도착하는 좌표의 예시를 보여주고 있고,
(b) 에서는 (a) 부분에서 마지막 경로인 2를 3으로 바꾸어 이동이 불가능한 상황의 예시를 보여주고 있다.
풀이
예전에 BOGGLE 문제를 풀 때 가졌던 아이디어를 몇가지 적용시켜봤다.
1. 재귀함수에 SOLVED 변수를 선언하여, 재귀를 도는 중에 이미 완료가 되었다면, 더이상 재귀를 실행하지 않음,
- 꽤 괜찮은 아이디어라고 생각한다. 앞으로 푸는 모든 알고리즘 문제에 재귀가 있다면 기본적으로 고려할만 하다.
2. 이미 지난 부분은 마킹한다.
- 이 문제의 복잡도는 최악의 경우 O(NN )이 된다. 행렬의 전체 경우의 수 가장 최악의 경우 모든 경로를 확인해야한다,
그 경우 경우의 수는 해당 식 (2nCn) 식과 같다.
지난 부분을 마킹하는 메모제이션을 활용하지 않는다면 이 문제는 시간초과가 걸릴 가능성이 높다.
여기서 조금 애매한 부분이 있는데, 어떤 좌표 (x,y)에서 이동할 수 있는 경우의 수는 오른쪽 혹은 아래 2가지가 있다.
즉 한번만 시도하고, 마킹을 해버리면, 해당 부분을 다시 확인하지 않기 때문에 문제가 생길 수 있다.
따라서 오른쪽 혹은 아래로 모두 재귀함수를 보낸 뒤에 메모제이션을 해야한다.
이런 문제를 풀다 보면, 역행 분석 (https://en.wikipedia.org/wiki/Retrograde_analysis) 을 하고싶은 생각이 들 수 있다.
즉 마지막 끝점에 도달 가능한 경로부터 찾은 뒤, 해당 경로부터 시작점을 되찾아 나가는 방법도 시도해볼 수 있다.
하지만, 문제의 케이스가 충분히 주어져 있는 한, 이러한 시도는 크게 의미가 있지 않다.
구현
코드 설명
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
TEST= int(input())
def finder(location):
global MEMORY
global GLOBAL_VARIABLE
# out of boundary
# 이미 찾았다면 종료
# 가고자 하는 방향이 이미 있다면
if GLOBAL_VARIABLE:
return -1
elif location[0]>= N or location[1]>= N:
return -1
elif MEMORY[location[0]][location[1]]==1:
return -1
elif location[0] ==N-1 and location[1] ==N-1:
GLOBAL_VARIABLE=1
return -1
else:
jump_size = MAP[location[0]][location[1]]
MEMORY[location[0]][location[1]]+=1
finder([location[0]+jump_size,location[1]])
MEMORY[location[0]][location[1]]+=1
finder([location[0],location[1]+jump_size])
for _ in range(TEST):
N = int(input())
MAP = []
MEMORY = [[] for x in range(N)]
GLOBAL_VARIABLE = 0
# MAP preparation
# MEMORY preparation
# MEMORY [X][Y] = [DOWN,RIGHT]
for index in range(N):
temp = input()
temp = temp.split(" ")
temp = [int(x) for x in temp]
MAP.append(temp)
for _ in range(N):
MEMORY[index].append(-1)
finder([0,0])
if GLOBAL_VARIABLE:
print("YES")
else:
print("NO")
|
cs |
매회 finder 재귀가 돌 때마다, GLOBAL VARIABLE을 참조하여, 이미 끝나지 않았다면 재귀를 지속한다.
경로의 바깥으로 가는 경우 해당 재귀를 정지시킨다 (return -1를 활용하여)
혹은 이미 갔던 경로일 경우 다시 가지 않는다.
눈치 채신 분도 있겠지만, elif의 순서는 가장 가능성이 높은 것부터 체크해주는 것이 좋다.
그래야 if문을 덜 돌기 때문,
재귀를 돌 때 가장 예상하기 쉬운 것은,
재귀를 돌아야 하는 경우 >= 재귀가 완료되었을 경우 >이미 해당 위치를 체크한 경우>=<바운더리 밖에 있을 경우 >위치에 도달한 경우 이다.
재귀를 돌아야하는 경우를 if문으로 설정하기 어려워 부득이하게 위치에 도달한 경우를 3번째에 넣었다.
위 코드의 결과는 아래와 같고,
바운더리 밖에 있을 경우>=< 이미 해당 위치를 체크한 경우 로 if 문의 순서를 바꾸면 아래와 같게 결과가 나온다.
따라서 재귀의 if문은 가능하다면 가장 많이 count가 되는 경우로 설정하는 것이 옳다.
'Develop > Algorithm' 카테고리의 다른 글
[Algorithm] Python3 Boggle 문제 풀이 (0) | 2019.12.15 |
---|