一、必胜策略
找规律策略
硬币游戏1
Alice和Bob在玩这样一个游戏。给定k个数字a[1],a[2],…,a[k]。一开始,有x枚硬币,Alice和Bob轮流取硬币。每次所取硬币的枚数一定要在a[1],a[2],…,a[k]当中。Alice先取,取走最后一枚硬币的一方获胜。当双方都采取最优策略时,谁会获胜?题目假定a[1],a[2],…,a[k]中一定有1。
int k,n; //k枚硬币 n个数字
int win[maxn];
void judge(){
win[0]=0; //轮到自己没硬币,失败
for (int i=1; i<=k; i++){
win[i]=0;
for (int j=0; j<n; j++){
win[i]|=a[j]<=i && !win[i-a[j]]; //让对手必败,则必胜
}
}
if (win[k]) cout<<"Alice\n";
else cout<<"Bob\n";
}
A funny game
n枚硬币排成一个圈。Alice和Bob轮流从中取一枚或两枚硬币。取两枚时,所取两枚必须连续。取走之后留下空位,相隔空位的硬币不连续。Alice开始先取,取走最后一枚硬币的一方获胜。当双方都采取最优策略时,谁会获胜?
无论自己采取什么策略,对手如果采取相同策略,
一定能回到相同情况,那么对手会取完最后一枚硬币,所以必败。
int n;
void solve(){
if (n<=2) cout<<"Alice\n";
else cout<<"Bob\n";
}
Euclid’s Game
给定两个整数a和b。Stan和Ollie轮流从较大的数字中减去较小的数字的倍数(正整数倍),并且相减后的结果不能小于零。Stan先手,在自己的回合将其中一个数变为零的一方获胜。当双方都采取最优策略时,谁会获胜?
假设a<b (a>b则交换)
假设b并非a的倍数,则有两种状态:
(1)b-a<a
(2)b-a>a
第二种状态总是必胜,所以从初始状态开始,最先达到第二状态的一方必胜
int a,b;
void solve(){
bool flag=true;
while (1){
if (a>b) swap(a,b);
if (b%a==0) break;
if (b-a>a) break;
b-=a;
flag=!flag;
}
if (flag) cout<<"Stan wins\n";
else cout<<"Ollie wins\n";
}
二、Nim Game(尼姆博弈)
Nim
有n堆石子,每堆各有ai颗石子。Alice和Bob轮流从非空的石子堆中取走至少一颗石子。Alice先取,取光所有石子的一方获胜。当双方都采取最优策略时,谁会获胜?
有以下结论成立:
a1 XOR a2 XOR…XOR an ≠ 0 ->先手必胜
a1 XOR a2 XOR…XOR an = 0 ->先手必败
int n,a[maxn];
void solve(){
int x=0;
for (int i=0; i<n; i++)
x^=a[i];
if (x!=0) cout<<"Alice\n";
else cout<<"Bob\n";
}
Staircase Nim
如图所示,排成直线的格子上放有n个棋子。棋子i在左数第pi个格子上。Georgia和Bob轮流选择一个棋子向左移动。每次可以移动至少一格,但是不允许反超其他棋子,也不允许两个棋子在同一格内。无法进行移动操作的一方失败。假设Georgia先进行移动,当双方都采取最优策略时,谁会获胜?
将棋子两两成对当作整体考虑,可以转化为Nim Game
(1)偶数情况下,从前往后两两一对,每队相当于Nim Game中的一堆石子,石子个数相当于两个棋子间的间隔。
注意,当左边棋子向左移动,石子数量增加,和Nim不同,但是右边移动到左边,左边再移动到左边,就回到了原来的状态,所以胜负状态和Nim的胜负状态相同
(2)奇数情况下,将最左边单独一个棋子与left断的间隔作为石子数。
int n,a[maxn];
void solve(){
if (n%2) a[n++]=0; //奇数时,在最左边添一个棋子
sort(a,a+n);
int x=0;
for (int i=0; i+1<n; i+=2){
x^=a[i+1]-a[i]-1;
}
if (x==0) cout<<"Bob will win\n";
else cout<<"Georgia will win\n";
}
三、Grundy数
int grundy(int x){
集合S={};
for(j=1:k){
if(a_j<=x) 将grundy(x-a_j)加到S集合中
}
return 最小的不属于S的非负整数
}
Grundy值:除任意一步所能转移到的状态的Grundy值以外的最小非负整数
Nim的XOR结论就转化为:
grundy(x1) XOR grundy(x2) XOR…XOR grundy(xn)
不为0必胜,为0必败
硬币游戏2
给定k个数字a1,a2,…,ak。一开始,有n堆硬币,每堆各有xi枚。Alice和Bob轮流选取一堆硬币,从中取出一些硬币。每次所取硬币枚数一定在ai中。Alice先取,取光的一方获胜。当双方都采取最优策略时,谁会获胜?
int N,K,X[maxn],A[maxk];
int grundy[maxx+1];
void solve(){
//轮到自己剩0的时必败
grundy[0]=0;
//计算grundy
int max_x=*max_element(X,X+N);
for(int j=1;j<=max_x;j++){
set<int> s;
for(int i=0;i<K;i++){
if(A[i]<=j) s.insert(grundy[j-A[i]]);
}
int g=0;
while(s.count(g)!=0) g++;
grundy[j]=g;
}
int ans=0;
for(int i=0;i<N;i++) ans^grundy[X[i]];
if(ans!=0) puts("Alice");
else puts("Bob");
}
Cutting Game
未完待续