문제 링크
https://www.acmicpc.net/problem/33517
문제
상혁이는 징검다리 게임을 하고 있다. 이 게임에는 개의 칸으로 구성된 징검다리가 있으며, 플레이어는 첫 번째 칸에서 출발하여 마지막 칸에 도달하면 게임에서 승리한다.
징검다리의 각 칸은 비어 있거나 곰 또는 지뢰 중 하나가 존재하며, 모든 곰의 체력은 이상의 정수이다. 또한 첫 번째 칸과 마지막 칸은 비어 있다. 징검다리 게임의 명령어는 아래와 같다.
- 점프 (J): 다음 칸으로 이동한다. 이동한 칸에 곰이나 지뢰가 있다면 플레이어는 패배한다.
- 공격 (A): 다음 칸에 있는 곰의 체력을 깎는다. 곰의 체력이 이 되면 다음 칸의 곰은 사라진다. 다음 칸이 빈칸이라면 아무 일도 일어나지 않으며, 다음 칸에 지뢰가 있다면 지뢰가 폭발하여 플레이어는 패배한다.
- 지뢰 제거 (D): 다음 칸에 있는 지뢰를 제거한다. 다음 칸에 지뢰가 없다면 아무 일도 일어나지 않는다.
상혁이는 징검다리 게임에서 승리하기 위해 키 매크로를 개발했다. 키 매크로는 점프(J), 공격(A), 지뢰 제거(D)로 구성된 길이 의 문자열을 입력받으면 문자열의 첫 번째 명령어부터 차례대로 실행한다. 마지막 명령어를 수행한 이후에는 첫 번째 명령어부터 다시 실행한다. 명령어를 회 실행했을 때 마지막 칸에 도달하지 못했다면 플레이어는 패배하며, 키 매크로는 실행 중 게임에서 승리하거나 패배할 경우 즉시 종료된다.
징검다리의 상태와 상혁이가 입력한 문자열이 주어졌을 때, 상혁이가 마지막 칸에 도달하여 게임에서 승리할 수 있다면 YES, 승리할 수 없다면 NO를 출력하시오.
오답 코드
from sys import stdin
input = stdin.readline
N = int(input())
A = list(map(int, input().split()))
M = int(input())
K = input().strip()
alive = True
pos = 0
limit = 10 ** 100
afk = 0
for i in range(limit):
cmd = K[i % M]
if pos+1 >= N-1:
if cmd == 'J':
break
else:
continue
if cmd == 'J':
if A[pos+1] != 0:
alive = False
break
else:
pos += 1
afk = 0
elif cmd == 'A':
if A[pos+1] > 0:
A[pos+1] -= 1
elif A[pos+1] == -1:
alive = False
break
afk += 1
else:
if A[pos+1] == -1:
A[pos+1] = 0
afk += 1
if afk == M:
alive = False
break
print("YES" if alive else "NO")
- 결과: 시간 초과 (TLE)
- 이유: K의 길이(M)가 최대 1,000,000이고 징검다리(N)가 500,000개이므로, 매번 명령어를 한 글자씩 확인하는 O(N × 필요한 명령어 수) 방식은 1초 안에 해결할 수 없다.
- 해결 방법: 일일이 명령어를 수행하는 것 대신 인덱스 점핑을 통해 다음에 어떤 인덱스의 징검다리 돌에 이동하게 되는지를 계산하면서 계산 횟수를 줄였다.
- 전처리: 명령어 문자열 K에서 J, A, D가 등장하는 위치(인덱스)를 각각 리스트에 저장한다.
- 위치 계산: 현재 매크로의 실행 위치가 pos일 때, n번째 뒤에 나오는 특정 명령어('A' 등)의 절대 위치를 계산하는 함수를 정의한다.
- 생존 판정:
- 곰(체력 H): H번의 'A'가 필요하다. H번째 'A'가 나오는 위치를 찾는다. 그 전에 'J'가 나온다면 패배한다.
- 지뢰: 1번의 'D'가 필요하다. 첫 번째 'D'가 나오는 위치를 찾는다. 그 전에 'J'나 'A'가 나온다면 패배한다.
- 빈칸: 장애물을 처리한 후(또는 원래 빈칸이면), 다음 'J'를 찾아 이동한다.
정답 코드
from bisect import bisect_left
from sys import stdin
input = stdin.readline
N = int(input())
A = list(map(int, input().split()))
M = int(input())
K = input().strip()
alive = True
pos = 0
if 'J' not in K:
print("NO")
exit()
JAD = {'J': [], 'A': [], 'D': []}
for i in range(M):
JAD[K[i]].append(i)
has_bear = False
for i in range(N):
if A[i] > 0:
has_bear = True
break
if has_bear and not JAD['A']:
print("NO")
exit()
has_mine = False
for i in range(N):
if A[i] == -1:
has_mine = True
break
if has_mine and not JAD['D']:
print("NO")
exit()
def find_target(char, cur, cnt):
jad = JAD[char]
L = len(jad)
if L == 0:
return float('inf')
start_pos = bisect_left(jad, cur % M)
rem = L - start_pos
if cnt <= rem:
return (cur // M) * M + jad[start_pos + cnt - 1]
else:
nxt_rem = cnt - rem
nxt_start = ((cur // M) + 1) * M
return nxt_start + ((nxt_rem - 1) // L) * M + jad[(nxt_rem - 1) % L]
for i in range(N-1):
if A[i+1] == -1:
d_pos = find_target('D', pos, 1)
j_pos = find_target('J', pos, 1)
a_pos = find_target('A', pos, 1)
if j_pos < d_pos or a_pos < d_pos:
alive = False
break
pos = d_pos + 1
elif A[i+1] > 0:
kill_pos = find_target('A', pos, A[i+1])
j_pos = find_target('J', pos, 1)
if j_pos < kill_pos:
alive = False
break
pos = kill_pos + 1
j_pos = find_target('J', pos, 1)
pos = j_pos + 1
print("대상혁" if pos > 10**100 else ("YES" if alive else "NO"))
이분 탐색 메서드 bisect_left의 동작 방식은 아래와 같다.
- 해당 값이 리스트에 있을 때: 해당 인덱스를 반환
- 해당 값이 리스트에 없을 때: 리스트 오름차순에 들어갈 인덱스를 반환
from bisect import bisect_left
arr = [1, 3, 5, 7, 9]
print(bisect_left(arr, 5)) # 2
print(bisect_left(arr, 2)) # 1'PS > 백준' 카테고리의 다른 글
| [백준] (2263) 트리의 순회 [Python] (0) | 2025.11.03 |
|---|---|
| [백준] (2644) 촌수계산 [C] [C++] (0) | 2025.10.27 |
| [백준] (31024) Filesystem [Python] (0) | 2025.10.25 |
| [백준] (1194) 달이 차오른다, 가자. [Python] (0) | 2025.10.25 |
| [백준] (34560) 브레인롯 챔피언십 [Python] (0) | 2025.10.05 |