문제
입력/출력
- 입력: 한 줄에 정수 N (3 ≤ N ≤ 1000)
- 출력: 선공이 이기면 1, 후공이 이기면 2
예시
접근 개요
- 핵심 관찰: 한 번의 선분 선택은 다각형을 두 부분으로 분할합니다. 그리고 선택된 두 꼭짓점은 이후 어떤 선분의 끝점으로도 사용할 수 없습니다. 즉, 각 부분 문제에서는 그 구간 내부의 꼭짓점들만 사용 가능합니다.
- 불변식: 양쪽 부분 문제는 서로 독립인 임partial game이므로, 전체 그런디 수는 두 부분 그런디 수의 XOR 입니다.
- 전이식: 양 끝점을 제외한 한쪽 구간 내부 꼭짓점 수를 a (0 ≤ a ≤ n-2)라 하면, 다른 쪽은 b = n-2-a이고, 그런디는
- \( g[n] = \mathrm{mex}\{\, g[a] \oplus g[b] \mid a=0..n-2,\ b=n-2-a \,\} \)
- 기저: \(g[0]=g[1]=0\) (더 이상 그을 수 없는 상태)
전이 흐름도 (Mermaid)
flowchart TD
A[현재 꼭짓점 수 n] --> B{a = 0..n-2}
B --> C[g[a] XOR g[n-2-a]]
C --> D[값들 집합]
D --> E[mex 집합 = g[n]]
알고리즘
- DP로 0..N까지의 그런디 수를 누적 계산
- seen 배열에 가능한 \(g[a]\oplus g[b]\)를 표시한 뒤 mex를 취함
- 최종 \(g[N] > 0\)이면 선공이 이김(1), 아니면 후공(2)
복잡도
- 시간: \(O(N^2)\) — 각 n마다 a를 0..n-2 순회
- 공간: \(O(N)\) — 그런디 테이블 저장
구현 (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // 더 많은 정보는 42jerrykim.github.io 에서 확인하세요.
#include <bits/stdc++.h>
using namespace std;
int main(){ios::sync_with_stdio(false);cin.tie(nullptr);
int N; if(!(cin>>N)) return 0;
vector<int> g(N+1,0);
if(N>=0) g[0]=0; if(N>=1) g[1]=0;
for(int n=2;n<=N;++n){
vector<char> seen(n+5,0);
for(int a=0;a<=n-2;++a){
int b=n-2-a;
int val=g[a]^g[b];
if(val<(int)seen.size()) seen[val]=1;
}
int mex=0; while(mex<(int)seen.size() && seen[mex]) ++mex;
g[n]=mex;
}
cout<<(g[N]?1:2) << '\n';
return 0;
}
|
구현 (Python)
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
| # 더 많은 정보는 42jerrykim.github.io 에서 확인하세요.
import sys
def solve():
data = sys.stdin.read().strip().split()
if not data:
return
N = int(data[0])
g = [0]*(N+1)
if N >= 1:
g[1] = 0
for n in range(2, N+1):
seen = [False]*(n+5)
for a in range(0, n-1):
b = n-2-a
val = g[a]^g[b]
if val < len(seen):
seen[val] = True
mex = 0
while mex < len(seen) and seen[mex]:
mex += 1
g[n] = mex
print(1 if g[N] else 2)
if __name__ == "__main__":
solve()
|
정당성 스케치
- 독립합 원리: 선택한 선분이 두 부분을 만들고, 이후 각 부분 내에서만 선택이 가능하므로 게임은 두 서브게임의 합입니다.
- 이런 임partial game은 스프라그–그런디 정리에 의해 전체 그런디 = 부분 그런디 XOR가 되어 전이식이 성립합니다.
- 기저 상태(0,1)에서는 어떤 선분도 추가할 수 없으므로 그런디가 0인 무승부(패배) 상태입니다.
코너 케이스 체크리스트
- N=3,4 같은 최소값에서 전이가 올바르게 작동하는지
- 매우 큰 N(최대 1000)에서 시간/공간 초과 없는지
- a=0 또는 a=n-2 처럼 한쪽이 비는 경우 처리
- 입력 개행/공백 처리, 출력 형식(개행) 확인
제출 전 점검
- 입출력 형식, 개행, 초기화 및 배열 범위 점검
- 그런디 기저값(g[0], g[1]) 확인
- a 순회 범위 0..n-2 확인, XOR/mex 구현
참고
- Sprague–Grundy theorem, impartial games 기초 자료