博弈论
巴什博弈:
只有一堆n个物品,两个人轮流从中取物,规定每次最少取一个,最多取m个,最后取光者为胜。
一共n=15个物品,每人轮流取1~4(m=4)个
先手: a1 a2 a3
后手: 5-a1 5-a2 5-a3
此时后手必赢 因为 n%(m+1)==0
而其他情况下先手都可以把n%(m+1)的余数先取掉,从而变成必胜条件。
#include <iostream>
using namespace std;
int main()
{
int n,m;
while(cin>>n>>m)
if(n%(m+1)==0)
cout<<"后手必胜"<<endl;
else cout<<"先手必胜"<<endl;
return 0;
}
Public Sale
这道题对输入的n、m有两种情况进行分类讨论。
第一种:n>m时,只要大于成本价m的价格都可以叫价,所以按顺序输出m到n就行;
第二种:n<m时,就变成了Lele和Yueyue从m个物品中每次取1~n个物品,取完者获胜,同时Lele是先手,那就根据巴什博弈讨论一下先手必胜和后手必胜的两种情况
当m%(n+1)==0时,后手必胜,Lele在第一次无论如何出价都无法买到这块土地;
当m%(n+1)!=0时,先手必胜,Lele需要在第一次将余数取掉,即叫价m%(n+1)。
#include<iostream>
using namespace std;
int main()
{
int m,n;
while(cin>>m>>n)
{
if(n>m)
{
for(int i=m;i<n;i++)
cout<<i<<" ";
cout<<n<<endl;
}
else
{
if(m%(n+1)==0) cout<<"none"<<endl;
else cout<<m%(n+1)<<endl;
}
}
return 0;
}
题目的意思是说 给出n*m的表盘,kiki作为先手要从右上角走到左下角,每次能向左走、向下走、向左下走、若能必胜输出“Wonderful!”,若必输则输出“What a pity!”。
然后可以多画几种情况来看一下:
这里用P表示kiki必胜的情况,N表示必败的情况。
若n=1,m=1,为图示表格右上角的位置,kiki已经不能走了,所以为P;
若n=2,m=1,为图示表格右上角“日”字形的两块,kiki作为先手要走到下面一块,此时为必胜。
…………
同理画出5*5的表格之后可以发现规律:当n、m有任意一个为偶数时,kiki都是必胜的。
#include<iostream>
using namespace std;
int main()
{
int n,m;
while(cin>>n>>m&&n&&m)
{
if(n%2==0||m%2==0)cout<<"Wonderful!"<<endl;
else cout<<"What a pity!"<<endl;
}
return 0;
}
威佐夫博弈:
有两堆各若干的物品,两人轮流从其中一堆取至少一件物品,至多不限,或从两堆中同时取相同件物品,规定最后取完者胜利。
结论:
若两堆物品的初始值为x,y且x<y令 z=y-x;记 temp=(int)[((sqrt(5)+1)/2)*z ];若 temp==x ,则先手必败,否则先手必胜。
int main()
{
int x,y,temp;
while(cin>>x>>y)
{
if(x>y) swap(x,y);
temp=floor((y-x)*(1+sqrt(5.0))/2.0);
if(temp==x) cout<<"后手必胜"<<endl;
else cout<<"先手必胜"<<endl;
}
return 0;
}
取石子游戏
此题为威佐夫博弈,直接用结论即可。
这道题的精度要求比较严格,1.618用(sqrt(5.0)+1)/2来表示
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
int a,b;
double x=(sqrt(5.0)+1)/2;
while(cin>>a>>b)
{
if(a>b)swap(a,b);
int ans= 1.0*(b-a)*x;
if(a==ans)cout<<"0"<<endl;
else cout<<"1"<<endl;
}
return 0;
}
最后引用大佬一张图
概括了三种最基本博弈的一般思路
尼姆博弈
目前有任意堆石子,每堆石子个数也是任意的,双方轮流从中取出石子,规则如下:
①每一步应取走至少一枚石子;每一步只能从某一堆中取走部分或全部石子;
②如果谁取到最后一枚石子就胜。
结论:
尼姆博弈,如果当前局面(a1,a2……an)中,a1⊕a2⊕……⊕an = 0,那么当前局面是必败态。 (⊕表示异或)
证明过程:https://blog.csdn.net/Hxj_CSDN/article/details/82932816
https://www.iteye.com/blog/ghods-2088667
例
Being a Good Boy in Spring Festival
//尼姆博弈,如果当前局面(a1,a2……an)中,a1⊕a2⊕……⊕an = 0,那么当前局面是必败态。
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=1e6+5;
int m,a[105],ans=0,cnt=0;
int main()
{
while(cin>>m)
{
ans=0;cnt=0;
if(m==0)break;
for(int i=0;i<m;i++)
{
cin>>a[i];
ans^=a[i];
}
if(ans==0)cout<<"0"<<endl;
else
{
for(int i=0;i<m;i++)//获胜种数
{
int t=ans^a[i];
if(a[i]>=t)cnt++;
}
cout<<cnt<<endl;
}
}
return 0;
}
反尼姆博弈
同尼姆博弈,就是把获胜条件反过来当异或值为1时为必败状态。
Be the Winner
斐波那契博弈
一般题目是在一堆n个数量的物品中两人轮流取,每次至少取1个,但是每次取的数量不能超过上次取的数目的两倍(n>=2且第一次取的时候不能取完)。
结论:n是斐波那契数的时候先手必败。
代码: HDU - 2516
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
int main()
{
LL f[60],n;
f[0]=1;f[1]=1;
for(int i=2;i<50;i++)
f[i]=f[i-1]+f[i-2];
while(cin>>n)
{
if(n==0)break;
if(binary_search(f,f+50,n))cout<<"Second win"<<endl;
else cout<<"First win"<<endl;
}
return 0;
}