博弈论

巴什博弈:

只有一堆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;
}

HDU - 2149

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;
}

HDU - 2147

## kiki's game

HDU - 2147

题目的意思是说 给出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;
}

取石子游戏

HDU - 1527

此题为威佐夫博弈,直接用结论即可。
在这里插入图片描述
这道题的精度要求比较严格,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

HDU - 1850

//尼姆博弈,如果当前局面(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

HDU - 2509


斐波那契博弈

一般题目是在一堆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;
}
posted @ 2020-07-29 15:51  神奇周一  阅读(768)  评论(0编辑  收藏  举报