백준 문제/삼성 기출
17837: 새로운 게임 2
goldpig
2024. 3. 28. 12:02
728x90
https://www.acmicpc.net/problem/17837
17837번: 새로운 게임 2
재현이는 주변을 살펴보던 중 체스판과 말을 이용해서 새로운 게임을 만들기로 했다. 새로운 게임은 크기가 N×N인 체스판에서 진행되고, 사용하는 말의 개수는 K개이다. 말은 원판모양이고, 하
www.acmicpc.net
설명
재현이는 주변을 살펴보던 중 체스판과 말을 이용해서 새로운 게임을 만들기로 했다. 새로운 게임은 크기가 N×N인 체스판에서 진행되고, 사용하는 말의 개수는 K개이다. 말은 원판모양이고, 하나의 말 위에 다른 말을 올릴 수 있다. 체스판의 각 칸은 흰색, 빨간색, 파란색 중 하나로 색칠되어있다.
게임은 체스판 위에 말 K개를 놓고 시작한다. 말은 1번부터 K번까지 번호가 매겨져 있고, 이동 방향도 미리 정해져 있다. 이동 방향은 위, 아래, 왼쪽, 오른쪽 4가지 중 하나이다.
턴 한 번은 1번 말부터 K번 말까지 순서대로 이동시키는 것이다. 한 말이 이동할 때 위에 올려져 있는 말도 함께 이동한다. 말의 이동 방향에 있는 칸에 따라서 말의 이동이 다르며 아래와 같다. 턴이 진행되던 중에 말이 4개 이상 쌓이는 순간 게임이 종료된다.
- A번 말이 이동하려는 칸이
- 흰색인 경우에는 그 칸으로 이동한다. 이동하려는 칸에 말이 이미 있는 경우에는 가장 위에 A번 말을 올려놓는다.
- A번 말의 위에 다른 말이 있는 경우에는 A번 말과 위에 있는 모든 말이 이동한다.
- 예를 들어, A, B, C로 쌓여있고, 이동하려는 칸에 D, E가 있는 경우에는 A번 말이 이동한 후에는 D, E, A, B, C가 된다.
- 빨간색인 경우에는 이동한 후에 A번 말과 그 위에 있는 모든 말의 쌓여있는 순서를 반대로 바꾼다.
- A, B, C가 이동하고, 이동하려는 칸에 말이 없는 경우에는 C, B, A가 된다.
- A, D, F, G가 이동하고, 이동하려는 칸에 말이 E, C, B로 있는 경우에는 E, C, B, G, F, D, A가 된다.
- 파란색인 경우에는 A번 말의 이동 방향을 반대로 하고 한 칸 이동한다. 방향을 반대로 바꾼 후에 이동하려는 칸이 파란색인 경우에는 이동하지 않고 가만히 있는다.
- 체스판을 벗어나는 경우에는 파란색과 같은 경우이다.
- 흰색인 경우에는 그 칸으로 이동한다. 이동하려는 칸에 말이 이미 있는 경우에는 가장 위에 A번 말을 올려놓는다.
체스판의 크기와 말의 위치, 이동 방향이 모두 주어졌을 때, 게임이 종료되는 턴의 번호를 구해보자.
핵심 아이디어
- 이 문제는 시뮬레이션 문제로, 주어진 조건을 잘 수행하면 정답 판정을 받을 수 있다.
- 모든 말에 대해서 이동을 수행 한 후 count 1 증가 시켜주고 턴의 횟수, 즉 count가 1000번이 넘으면 게임을 강제로 종료시킨다. 또한 말을 이동하면서 말이 4개이상 쌓이는 순간(False로 check) count를 출력하고 게임을 종료한다.
핵심 구현 함수
# 시뮬레이션 구현 함수
def move(h_number): # 이동하려는 말의 번호가 들어온다.
# 이동하려는 말의 현재 위치와 방향
x,y,d = horse[h_number]
# 다음 이동칸 정의
nx = x + dx[d]
ny = y + dy[d]
if nx < 0 or nx >=n or ny < 0 or ny >=n or graph[nx][ny] == 2:
# 방향을 반대로
d = change_dir(d)
# 업데이트
horse[h_number][2] = d
# 다음 칸 재정의
nx = x + dx[d]
ny = y + dy[d]
if nx < 0 or nx >=n or ny < 0 or ny >=n or graph[nx][ny] == 2:
# 이동하지 않는다.
return True
# 현재 말~그 위에 쌓였는 말에 대한 정보를 담을 배열
horse_up = []
for h_idx, h_n in enumerate(chess[x][y]): # 모든 말을 순회
if h_n == h_number:
# 해당 말 위에 있는 말까지 포함해서 저장
horse_up.extend(chess[x][y][h_idx:])
# 이동을 위해 현재 chess 정보에서 제거해 준다.
chess[x][y] = chess[x][y][:h_idx]
break
# 다음 칸이 빨간색 체스판이라면 순서를 반대로 돌린다.
if graph[nx][ny] == 1:
horse_up = horse_up[-1::-1]
# 말 이동 시키기
for h in horse_up: # h: 말의 번호
# 이동 시킨다.
horse[h][0], horse[h][1] = nx,ny
chess[nx][ny].append(h) # 말 ~ 해당 말 위 모든말 한번에 이동
# 이제 4마리 이상 쌓여 있으면
if len(chess[nx][ny]) >= 4:
return False
return True
- 이동하려는 다음 칸이 체스판 범위를 넘거가거나 파란색인 경우, change_dir() 함수를 활용하여 방향을 바꾼다. 바꾼 상태에서 다시 다음 칸으로 이동하려고 할 때 또, 범위를 벗어나거나 파란색이면 이동을 하지 않는다.
- 이동하기 전에 '현재 위치'에서 chess 배열을 통해 horse_up 리스트에 현재 이동하려는 말 그리고 해당 말 위에 있는 말에 대한 정보를 담는다(extend()활용). 또한 말을 이동하기 위해 chess 리스트에서 horse_up에 저장한 말에 대한 정보를 지운다.
- 만약 이동하려는 칸이 빨간색이면 horse_up 리스트에 들어있는 말의 순서를 뒤바꾼다.
- 이동하려는 위치에 horse_up 리스트로 '현재 이동하려는 말 + 위에 있는 말의 번호'를 chess판에 넣어주고, 이동된 말들의 위치 정보를 변경해 준다.
- 이동이 끝났을 때, 쌓인 말이 4개 이상이라면 게임을 종료할 수 있게 False를 반환한다.
코드 구현
# 맵의 크기와 말의 개수 입력받기
n, k = map(int, input().split())
# 체스판 정보 입력 받기.
# 0: 흰색 1: 빨강 2: 파랑
graph= [list(map(int, input().split())) for _ in range(n)]
# 방향 정의: 동 서 북 남 -> 1 2 3 4
dx = [0,0,-1,1]
dy = [1,-1,0,0]
# 말의 위치와 방향을 담을 리스트
horse = []
# [말의 번호] 를 저장할 리스트
chess = [[[] for _ in range(n)] for _ in range(n)]
for i in range(k):
x,y,d = map(int, input().split())
horse.append([x-1,y-1,d-1]) # 인덱스 재조정
# 체스판 위치에 말의 번호를 저장한다.
chess[x-1][y-1].append(i)
# 턴 횟수 초기화
count = 0
# 이동 방향을 반대로 바꾸는 함수
def change_dir(d):
if d in [0, 2]: # 동 북
d += 1
elif d in [1, 3]: # 서 남
d -= 1
return d
# 말을 이동해서 시뮬레이션 시작
def move(h_num): # 말의 번호가 들어온다.
x,y,d = horse[h_num] # 해당 번호 말의 현재 위치와 방향
nx = x + dx[d]
ny = y + dy[d]
# 격자 범위를 벗어나거나 파란색인 경우
if nx < 0 or nx >=n or ny < 0 or ny >=n or graph[nx][ny] == 2:
# 방향을 반대로 바꾼다.
d = change_dir(d)
horse[h_num][2] = d # 방향 업데이트
nx = x + dx[d]
ny = y + dy[d]
# 방향을 반대로 바꾼 후에 다음 칸이 또 파란색이면 이동하지 않고 멈춘다.
if nx < 0 or nx >=n or ny < 0 or ny >=n or graph[nx][ny] == 2:
return True
# 이동하기 전 현재위치에서 chess 배열을 통해 현재 이동하려는 말 그 위에
# 쌓아져 있는 말에 대한 정보를 horse_up에 저장한다.
horse_up = []
# 현재 위치 (x,y)에서 각 말의 '인덱스와 번호'를 순회하면서 제공
for h_idx, h_n in enumerate(chess[x][y]):
# 현재 말의 번호가 찾고자 하는 말의 번호와 일치하다면
if h_n == h_num:
# 현재 말(h_n)과 그 위에 모든 말들을 horse_up 리스트에 추가
# chess[x][y][h_idx:] -> 현재 말의 위치(h_idx)에서 리스트의 끝까지
horse_up.extend(chess[x][y][h_idx:])
# 현재 이동하려는 말 ~ 그 위에 쌓아져 있는 말에 대한 번호를 지운다.
chess[x][y] = chess[x][y][:h_idx]
break
# 만약 빨간색이라면 즉 1이라면
if graph[nx][ny] == 1:
# 현재 말과 그 위에 있는 모든 말의 쌓여있는 순서를 반대로 바꾼다.
horse_up = horse_up[-1::-1] # == horse_up.reverse()
'''
이동하려는 위치 nx,ny에 horse_up 리스트로 현재 이동하려는 말~ 그 위에
쌓여져 있는 말의 번호를 chess판에 넣어주고, horse 배열에 이동된
말들의 위치 정보를 변경해 준다,
'''
# 특정 말과 그 위에 있던 모든 말들을 새로운 위치로 이동시킨다.
for h in horse_up:
horse[h][0], horse[h][1] = nx, ny
# 이동한 후 현재 말의 번호를 저장
chess[nx][ny].append(h)
# 만약 쌓여있는 말의 개수가 4개 이상이라면 종료
if len(chess[nx][ny]) >= 4:
return False
return True
while True:
check = False
if count > 1000:
print(-1)
break
for i in range(k):
if move(i) == False: # 강제 종료됐다면
check = True
break
count += 1
if check: # 이동 했다면
print(count)
break
빨간색 공 조건에서 horse_up = horse_up[-1::-1] 대신 reverse() 함수를 사용해도 가능. horse_up.reverse()
728x90