先给出三大博弈的定义和基本做法。
巴什博弈:只有一堆共n个石子,两人轮流取,最少取一个,最多取m个,最先取光石子的人赢。 做法:求n%(m+1),如果等于0先手必输,不等于0则先手必赢。
威佐夫博弈:有两堆各x,y个石子,两人轮流从某一堆或同时从两堆中取同样多的石子,规定每次至少取一个,多者不限,最先取光石子的人赢。 做法:若两堆物品的初始值为(x,y),且x< y,记w=(int)((sqrt(5.0)+1.0)/2.0∗(y-x));如果w=x则先手必输,w!=x则先手必胜。
尼姆博弈:有n堆石子(可假设分别为a[1]~a[n]个石子),两人轮流从某一堆取任意多的石子,规定每次至少取一个,多者不限,最先取光石子的人赢。 做法:将所有堆的石子异或起来,如果等于0则先手必输,不等于0则先手必赢。
其他概念:先手必输为P-position,先手必赢为N-position
HDU2147 巴什博弈 博弈论:组合博弈 必败点(P点) :前一个选手(Previous player)将取胜的位置称为必败点。 必胜点(N点) :下一个选手(Next player)将取胜的位置称为必胜点。 现在关于P,N的求解有三个规则 (1):最终态都是P (2):按照游戏规则,到达当前态的前态都是N的话,当前态是P (3):按照游戏规则,到达当前态的前态至少有一个P的话,当前态是N hdu 1846 Brave Game 巴什博弈模板题。 如果n%(m+1)不为0可以先选n%(m+1),之后每次通过对方选的个数X选m+1-X个保证取到最后一个,此时先手必胜,相反后手可以控制这个过程所以后手必胜。
#include
using namespace std;
int main()
{
int n,m,t;
cin>>t;
while(t--)
{
cin>>n>>m;
if(n%(m+1)==0)printf("second\n");
else printf("first\n");
}
return 0;
}
poj 1067 取石子游戏 威佐夫博弈模板题
#include
#include
#include
using namespace std;
int main()
{
int x,y,w;
ios::sync_with_stdio(false);
while(cin>>x>>y)
{
w=(int)((sqrt(5.0)+1.0)/2.0*fabs(y-x));
if(w==(min(x,y)))printf("0\n");
else printf("1\n");
}
return 0;
}
poj 2234 Matches Game 尼姆博弈模板题
#include
#include
using namespace std;
int main()
{
int m,x,ans;
while(cin>>m)
{
ans=0;
while(m--)
{cin>>x;ans=ans^x;}
if(ans==0)printf("No\n");//所有石子异或为0,先手必输
else printf("Yes\n");
}
return 0;
}
poj 2975 Nim 尼姆博弈。 给定nim游戏的一种局面,你是A,对手是B,问你现在这一步有多少种取法使得你进入必胜态。 先求出原状态下所有石子的异或,设为tmp。判断tmp,如果tmp=0,你开始取石子,相当于你是先手,必输,即获胜的方法数为0,直接输出0即可。 如果tmp!=0,那么在你取了之后,就是B取,可以看成你取了石子(必须至少取一个)改变状态后,才正式开始游戏,这时可以把B看作先手,B开始在你取了石子之后的新状态下开始取。要使在你取了石子后的新状态下,作为先手的B必输,也就是要上一步你取了石子之后,所有石子异或为0,此时B必输,A必赢。 遍历a[i],上一步你只能选择一堆石子a[i]取走任意个石子,除a[i]以外的所有石子异或 tmp^a[i] 不变。要在a[i]中取走任意个石子后B必输,也就是要保证上一步你取了石子之后所有石子异或为0,也就是要在a[i]中取任意个石子后剩下的数量为tmp^a[i],那么只要 tmp^a[i]
#include #include using namespace std; int main() { int n,i,tmp,cnt,a[1001]; ios::sync_with_stdio(false); while(cin>>n&&n) { tmp=cnt=0; for(i=1;i<=n;i++) {cin>>a[i];tmp=tmp^a[i];} if(tmp==0){printf("0\n");continue;}//tmp=0说明在你取石子之前所有石子异或为0,此状态先手必输,这时你是先手,必输 for(i=1;i<=n;i++)