Codetree. 승자독식 모노폴리
1. 문제설명
NxN
크기의 격자칸에서 모노폴리 게임을 진행한다.M
개의 플레이어로 구성되어 있고, 각각 격자칸에서 한 자리를 차지한다.- 게임의 규칙
- 턴이 한번 진행될 때 각 플레이어들이 한칸씩 동시에 이동한다.
- 이동한 칸을
K
턴동안 독점 계약한다. - 초기 상태에 플레이어가 위치한 땅 역시 해당 플레이어가 독점 계약한 칸이다.
- 이동한 칸을
K
턴이 지나면 다시 주인이 없는 땅으로 돌아간다.- 플레이어의 이동
- 각 플레이어는 이동 우선순위를 갖는다.
- 인접한 상하좌우 4칸 중 아무도 독점계약을 맺지 않은 칸으로 이동
- 만약 이러한 칸이 없는 경우 본인이 독점계약한 땅으로 이동
- 이러한 칸이 여러 칸 이라면 이동 우선순위에 따라 칸을 결정
- 이동 후 하나의 땅에 두명 이상의 플레이어가 있는 경우, 번호가 가장 작은 플레이어만 살아남는다.
- 턴이 한번 진행될 때 각 플레이어들이 한칸씩 동시에 이동한다.
- 위 과정이 계속 반복될 때 1번 플레이어만 살아남기까지 걸린 턴의 수를 계산하라
2. 입/출력
- 입력
- 격자의 크기
N
, 플레이어의 수M
, 계약 턴 수K
2 ~ N+1
번째 줄까지 격자의 정보0
은 빈칸, 자연수는p
번 플레이어의 위치
N+2
번째 줄에는 각 플레이어의 초기 방향d
가 주어진다.- 1, 2, 3, 4 각각은 위, 아래, 왼쪽, 오른쪽
N+3 ~ M*4
번째 줄에는 각 플레이어의 방향에 따른 이동 우선순위- 첫번째 줄은 위를 향했을 때의 이동 우선순위, 두번째 줄은 아래를 향했을 때의 우선순위, …
- 격자의 크기
- 출력 : 1번 플레이어만 남게 되기까지 걸린 턴의 수
3. 문제 분류
- Simulation
4. 알고리즘 설계
- 자료형 및 변수 선언
- 플레이어의 정보를 담기 위한 구조체
info
alive
: 플레이어가 죽었다면false
dir
: 최초 방향dirs
: 입력받은 우선순위 방향 (4x4)
- 플레이어의 위치
(x, y)
를 찾기 위한 배열pos
- 맵의 정보를 담은 배열
board
first
: 플레이어 번호second
: 해당 칸의 남은 턴 수
- 플레이어의 정보를 담은 배열
players
-
플레이어가 이동한 후 좌표 정보
nxt_board
struct info { bool alive; int dir; vector<vector<int>> dirs; }; pii pos[SZ * SZ]; pii board[SZ][SZ]; vector<info> players; vector<pii> nxt_board[SZ][SZ];
- 플레이어의 정보를 담기 위한 구조체
- 프로세스 총 1000번 실행
if(1번 플레이어가 죽었다면) break;
if(1번을 제외한 모든 플레이어가 죽었다면) 정답값 = 실행 횟수; break;
- 플레이어 이동
- 턴 감소
- 플레이어 킬
- 플레이어의 죽음 여부
// alive는 플레이어의 생존 여부를 담고있는 boolean 값
for (int i = 1; i <= M; i++)
if (players[i].alive) return false;
return true;
- 플레이어 이동
- 핵심 부분을 살펴보면
- 처음에는 주변에 0인 칸이 있는지 찾고
- 없다면 -1이 리턴되어 본인이 방문했던 칸을 찾는다.
- 참고로 본인이 방문했던 칸이 없을 수는 없다.
- 처음에 모든 플레이어의 위치가 독립적이므로
for(cur : players) {
if(cur.alive) continue // 죽었으면 탐색하지 않음.
/**이 부분이 핵심!**/
nd = next_direction(i, 0); // 주변에 아무도 들르지 않은 칸(0)이 있다면
if(nd == -1) nd = next_direction(i, i); // 아무도 들르지 않은 칸이 없다면 본인이 방문했던 칸
nx = x + dx[nd];
ny = y + dy[nd];
nxt_board[nx][ny].push_back({i, k});
cur.dir = nd;
}
int next_direction(int idx, int target) {
int x = pos[idx].x;
int y = pos[idx].Y;
int nd = players[idx].dir;
for(int dir : 1~4) {
int ndir = cur.dir[nd][dir];
int nx = x + dx[ndir];
int ny = y + dy[ndir];
if(out of memory) continue;
if(board[nx][ny].first == target) return ndir;
}
return -1;
}
- 턴 감소
board[i][j]
에는{지나간 플레이어의 번호, 남은 턴 수}
가 기록되어 있다.- 남은 턴 수가 0보다 크면 1을 빼주고, 이 값이 0이 되었다면 플레이어의 번호를 0으로 만들어준다.
- 플레이어 킬
- 앞서
2. 플레이어 이동
에서nxt_board
에 다음 보드판을 기록하였다. nxt_board[x][y]
의 원소의 수가 0보다 크다면 해당 위치에 플레이어가 있다는 의미
- 1명의 플레이어만 있으면
board
,pos
를 갱신해주면 되고 - 2명 이상이면 플레이어의 번호가 적은순으로 정렬해 0번째 인덱스를 제외한 나머지 플레이어의
alive
를false
로 지정한다. - 이렇듯nxt_board
의 값은 매 턴마다 초기화 되어야 한다.
- 앞서
5. 전체 코드
6. 소감
- 문제를 잘못 이해해서 오랜시간 맞왜틀을 시전했다.
- 매순간마다 방향이 바뀌는 게 핵심이었다.
- 즉, 현재 바라보고 있는 방향인
players[i].dir
번째의 우선순위 방향을 통해 움직일 때마다 방향이 바뀔 수 있다.
- 즉, 현재 바라보고 있는 방향인