都是满足条件,则后手必胜
1.巴什博奕
- 场景:只有一堆n个物品,两个人轮流从中取物,规定每次最少取一个,最多取m个,最后取光者为胜。
- 解决:一轮最多拿的就是1+m个,所以控制下去,最后的不到(1+m)的物品肯定会被后手拿到的。
2.威佐夫博弈
- 场景:有两堆各若干的物品,两人轮流从其中一堆取至少一件物品,至多不限,或从两堆中同时取相同件物品,规定最后取完者胜利。
- 解决:(黄金分割)看两个数的差值t是不是满足 (sqrt(5)+1)/2*t==min(n1,n2);
3.nim博弈(异或问题)
- 场景:有任意堆物品,每堆物品的个数是任意的,双方轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取一件,取到最后一件物品的人获胜。
- 解决:把每堆物品数全部异或起来,如果得到的值为0,那么先手必败,否则先手必胜。
4.斐波那契博弈
- 场景:有一堆物品,两人轮流取物品,先手最少取一个,至多无上限,但不能把物品取完,之后每次取的物品数不能超过上一次取的物品数的二倍且至少为一件,取走最后一件物品的人获胜。
- 解决:先手胜当且仅当n不是斐波那契数(n为物品总数)
5.环形博弈
- 场景:n个石子围成一个环,每次取一个或者取相邻的2个。
- 解决:石子数目小于等于2 先手胜,其他 后手胜。
6. 对称博弈
-
场景:n个石子围成环,每次只能取相邻的1 - k个
①如果k < n: 对k=1,如果n能被2整除,则后手赢 如果k>1,后手赢(先手取什么位置后手就取对称的位置,这样保证后手永远能取到) ② 如果k>=n:先手赢.
ICG
1.定义:整个游戏可以抽象成一个DAG;
每个点都代表游戏过程中的某个决策状态(特殊的,出度为0的点是游戏的终止状态);
每条边都表示可以从某个状态,经过直接的一次操作,转移到另一个状态。
有两个玩家,从指定的某个状态开始,依次执行操作,谁先因为当前到达了终止状态而无法操作的人输。
- 解决:可以对每个点标记两种状态N和P,分别表示先手必胜和后手必胜。首先根据定义,终状态为P,然后,所有能直接到达终状态的状态就为N了。进一步推广,如果某点的出边指向的点的集合中,有至少一个点为P,那么这个点为N;否则就为P。
- 可以看作一颗树,叶节点为p,一个节点的子节点中,有p则自身为n,无p则自身为p。因为当前节点可以决定走向哪一个子节点。
SG函数
1.若干ICG拼凑在一起,成为一个规模更大的SG模型
2. 定义运算mex(S)(S是一个自然数集合),结果为S中未出现的最小自然数。
定义SG(i)(i是一个状态,在图中是一个点)为对所有i可直达状态(在图中与i通过有向边连接的所有点)的SG函数值取mex的值,即: SG(i)=mex(j∣SG(j),(i,j)∈G)
定义SG定理:点i为P当且仅当SG(i)=0
若干ICG(可以不相同)组合在一起的游戏,把每个游戏的SG值异或起来,不为0则先手必胜。
板子
//f[]:可以取走的石子个数
//sg[]:0~n的SG函数值
//vis[]:mex{}
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int T,n,m,x,f[10100],a[10010],sg[100010];
bool vis[100010];
void getSG(int n)
{
memset(sg,0,sizeof(sg));
for (int i=1; i<=n; i++)
{
memset(vis,0,sizeof(vis));
for (int j=0; f[j]<=i; j++)
vis[sg[i-f[j]]]=1;
for (int j=0; j<=n; j++)
if (!vis[j])
{
sg[i]=j; break;
}
}
}
int main()
{
for (int i=1; i<=10001; i++) f[i]=i;
getSG(10000);
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
int ans=0;
for (int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
ans^=a[i];
}
if (ans) printf("Yes\n");
else printf("No\n");
}
return 0;
}
补充:
1.阶梯nim的奇数层异或,最低是0层
埋坑(还没写出来)
【Todo】洛谷P2594 [ZJOI2009]染色游戏
【Todo】洛谷P3185 [HNOI2007]分裂游戏
参考
大佬