我们认为在当前局面下玩家无法操作的局面是失败局面
例题1:引入
有一堆 n n n 个石子 , 现给定集合 { s 1 . . . s k } \lbrace s_1...s_k\rbrace {s1...sk} , 每次可以取 s i ( 1 ≤ i ≤ k ) s_i(1\le i\le k) si(1≤i≤k) 颗 , 问先手是否必胜 ( n , k ≤ 1000 ) (n,k\le 1000) (n,k≤1000)
我们记 f x f_x fx 表示 x x x 颗石子的情况下 , 先手是否必胜
那么只要存在 i i i 使得 f x − s i f_{x-s_i} fx−si 为 f a l s e false false , 先手就可以拿走 s i s_i si 颗石子 , 使对手遇到 f x − s i f_{x-s_i} fx−si 的局面 , 从而取胜
int dfs(int x)
{
if(x==0) return 0;
if(mem[x]) return f[x];
mem[x] = 1;
for(int i=1;i<=k;i++)
{
if(x>=s[i]) f[x] |= (!dfs(x-s[i]));
}
return f[x];
}
cout << dfs(n);
我们也可以将其看作是一个只有一个起点的有向图游戏 , 一开始是 n n n , 边权为 − s i -s_i −si
有向图游戏 : 在一个 D A G DAG DAG 中,只有一个起点,上面有一个棋子,两个玩家轮流沿着有向边推动棋子,不能走的玩家判负
例题2 : Nim博弈模板
Nim游戏 : n n n 堆石子 , 每堆 a i a_i ai 个 , 每次从第 i i i 堆取 1 1 1 到 a i a_i ai 个石子 , 问先手是否必胜 ( n , s i ≤ 1 0 4 ) (n,s_i\le10^4) (n,si≤104)
若我们延续 例题1 的思路 , 发现时间复杂度和空间复杂度都会很高 , 而此题有一个结论 :
当且仅当 a 1 ∧ a 2 ∧ . . . . . a n = 0 a_1\land a_2\land .....a_n=0 a1∧a2∧.....an=0 时 , 先手无必胜策略
证明 :
结论1:
当 a 1 ∧ a 2 ∧ . . . . . a n = 0 a_1\land a_2\land .....a_n=0 a1∧a2∧.....an=0 时 , 无论怎样取 , 其异或和必不等于 0 0 0
证明1:
反证法 : 假设存在将第 i i i 堆从 a i a_i ai 取成 a i ′ a_i' ai′ 的方案使得异或和为 0 0 0
那么 , a 1 ∧ . . . ∧ a i ′ ∧ . . . ∧ a n = 0 a_1\land ...\land a_i' \land...\land a_n=0 a1∧...∧ai′∧...∧an=0 且 a 1 ∧ . . . ∧ a i ∧ . . . ∧ a n = 0 a_1\land ...\land a_i \land...\land a_n=0 a1∧...∧ai∧...∧an=0 , 得 a i = a i ′ a_i=a_i' ai=ai′ 即取了 0 0 0 颗 , 不合题意 , 矛盾
结论2:
当 a 1 ∧ a 2 ∧ . . . . . a n ≠ 0 a_1\land a_2\land .....a_n\ne 0 a1∧a2∧.....an=0 时 , 总存在取法使得异或和变为 0 0 0
证明2:有
设 a 1 ∧ a 2 ∧ . . . . . a n = x a_1\land a_2\land .....a_n= x a1∧a2∧.....an=x
我们设 x x x 二进制下最高位 1 1 1 在第 k k k 位 , 那么至少存在一堆石子 a i a_i ai 的第 k k k 位为 1 1 1 , 那么 a i ∧ x < a i a_i\land x
因此在 a 1 ∧ a 2 ∧ . . . . . a n ≠ 0 a_1\land a_2\land .....a_n\ne 0 a1∧a2∧.....an=0 的局面下 , 先手可将局面变为异或和为 0 0 0 的局面 , 而后手只能又将异或和变为非零 , 一直循环至全部取完
而全部取完时, a 1 ∧ a 2 ∧ . . . . . a n = 0 a_1\land a_2\land .....a_n=0 a1∧a2∧.....an=0 , 为必败局面 , 而只有后手才会遇到异或和为零的局面 , 因此先手在此情况下必胜 , 证毕.
cin >> n;
int sum = 0;
for(int i=1;i<=n;i++)
{
cin >> a[i];
sum ^= a[i];
}
if(!sum) puts("No");
else puts("Yes");
例题3: 取火柴(Nim博弈)
此题要求输出先手取胜策略的第一步
如上文分析 : 若 a 1 ∧ a 2 ∧ . . . . . a n = x a_1\land a_2\land .....a_n=x a1∧a2∧.....an=x , 先手第一次取后应使得异或和变为 0 0 0 , 即将某个 a i a_i ai 取成 a i ∧ x a_i\land x ai∧x
根据题目要求 , 找到 i i i 最小的 a i ∧ x < a i a_i\land x
定义1: m e x mex mex
m e x ( S ) = m i n { x } mex(S)=min\lbrace x\rbrace mex(S)=min{x} ( x x x 不属于 S S S , x x x 为非负整数)
定义2: 有向图游戏
例题1 中已经阐释过 , 例题1 就是一个只有一个起点的有向图游戏
定义3: SG函数
对于状态 x x x 和他的所有 k k k 个后继状态 y 1 . . . y k y_1...y_k y1...yk , S G ( x ) = m e x { S G ( y 1 ) , . . . . S G ( y k ) } SG(x)=mex\lbrace SG(y_1),....SG(y_k)\rbrace SG(x)=mex{SG(y1),....SG(yk)}
SG定理 而对于由 n n n 个有向图游戏组成的组合游戏,设它们的起点分别为: s 1 , s 2 , . . . . s n s_1,s_2,....s_n s1,s2,....sn , 当且仅当 S G ( s 1 ) ∧ S G ( s 2 ) ∧ . . . ∧ S G ( s n ) ≠ 0 SG(s_1)\land SG(s_2)\land ...\land SG(s_n)\ne 0 SG(s1)∧SG(s2)∧...∧SG(sn)=0 时 , 先手必胜
例题4 :SG定理模板
n n n 堆石子 , 分别为 a 1 , a 2 . . . a n a_1,a_2...a_n a1,a2...an , 给定 { s 1 . . s k } \lbrace s_1..s_k\rbrace {s1..sk} 每次只能取 s i s_i si 个
看作是一个 n n n 个起点的有向图游戏 , 考虑使用 SG定理 , 求出每个起点的 S G SG SG 即可 ( 这里是 S G ( a 1 ) , S G ( a 2 ) . . . S G ( a 3 ) SG(a_1),SG(a_2)...SG(a_3) SG(a1),SG(a2)...SG(a3) )
int dfs(int x)
{
if(x==0) return 0;
if(mem[x]) return SG[x];
mem[x] = true;
bool v[105]; memset(v,0,sizeof(v));
for(int i=1;i<=k;i++)
{
if(x>=s[i]) v[dfs(x-s[i])] = 1;
}
int i = 0; while(v[i++]);
return SG[x] = i-1;
}