博弈论
(一)、acm博弈基础算法
Bash Game,Nim Game和Wythoff Game(即 巴什博奕、尼姆博弈、威佐夫博弈)
Bash Game: 同余理论
Nim Game: 异或理论
Wythoff Game: 黄金分割
(二)、三个博弈。
1、巴什博奕。
只有一堆n个物品,两个人轮流从这堆物品中取物,
规定每次至少取一个,最多取m个。最后取光者得胜。
如果n=m+1,那么由于一次最多只能取m个,
所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。
只要保持给对手留下(m+1)的倍数,就能最后获胜.
while(~scanf("%d%d",&n,&m)) { if(n%(m+1)==0)//必输 puts("lose"); else puts("win"); }
巴什博奕练习题HDU2149
代码:
//HDU2149巴什博奕
#include<stdio.h>
int main()
{
	int m,n;//土地价值m,每次最多取n个 
	while(~scanf("%d%d",&m,&n))
	{
		if(m%(n+1)==0)//必输 
			puts("none");
		else
		{//要给对手留下奇异局势(必输局势) 
			int flag=1;
			for(int i=1;i<=n;i++)//枚举
			{
				int mk=m-i<0? 0:m-i;
				if(mk%(n+1)==0)//对手面对奇异局势
				{
					if(flag)
						printf("%d",i),flag=0;
					else
						printf(" %d",i); 
				}
			}
			puts("");
		}
	}
	return 0;
 }2、尼姆博弈
有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,
规定每次至少取一个,多者不限,最后取光者得胜。
尼姆博弈说的是三堆,但是他的方法却可以推广到任意堆。
尼姆博弈我只会用它的公式,至于原理,,搞不了
即,把所有堆的数量亦或起来,若为0,则先手必输
还要知道一个常识:a, b, a ^ b 三个数之间,两两亦或可得第三个数
分析:
存在一些奇异局势,会使得先手必输,如下面的情况(三堆时)
(0,0,0),(0,n,n)
只要面对这种局势,必输。
任何奇异局势(a,b,c)都有a(+)b(+)c =0。
获胜策略: 所以从一个非奇异局势向一个奇异局势转换的方式可以是:
1)使 a = c(+)b
2)使 b = a(+)c
3)使 c = a(+)b
(14,21,39),14(+)21=27,39-27=12,
所以从39中拿走12个物体即可达到奇异局势(14,21,27)。
尼姆博弈练习题:HDU1850
分析:该题在求得先手是否有必赢策略的基础上,若能赢,输出方案数。
假如每一堆都可以取,取了之后必胜,方案数最多最多等于m(堆数)
看上面的获胜策略,想要留给对手奇异局势,就必须在某一堆取出一些,
使剩下的局势成为奇异局势。
假如我取第c堆想赢,就要保证c的数量大于其他堆的异或结果,才够减
等于不行,等于的话相当于把第c堆取完,
那么其余堆的总亦或不为0,这就给了对手有必赢策略的机会
代码:
 
3、威佐夫博弈
有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,
规定每次至少取一个,多者不限,最后取光者得胜。
分析:
对于给出的两堆石子,记为a个和b个(a<=b),(a,b)。
存在一些特殊情况,当你面对下面一些情况时,必输。
(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)。
先看(0,0),若你目前面对这个局势,说明你的对手抢先一步赢了。
再看(1,2),不管你怎么取,你取完后,剩下只可能是(0,1)(0,2)(1,1),对手就会赢
上面的情况称为奇异局势(即 必输)。可以通过下面的公式得到这些奇异局势
a = [ k * eqa ] ([ ] 代表取整数部分)
b = a + k
其中epa = (1+√5)/2= 1.618033 = (1+sqrt(5.0))/2.0
可以用这个通项公式打表:
#include<stdio.h>
#include<math.h>
const int N=1000;
const double eqa=(1+sqrt(5.0))/2.0;
int a[N],b[N];
void makelose()
{
	for(int k=0;k<N;k++)
	{
		a[k]=(int)(k*eqa);
		b[k]=a[k]+k;
	}
} 
  
 也可以判断任意给出的(a,b)是不是奇异局势 
  
scanf("%d%d",&a,&b); 
	if(a>b)
	{
		a=a^b;
		b=a^b;
		a=a^b;
	}
	int k=b-a;
	if(a==(int)(k*eqa))
		puts("0");//是奇异局势,必败
	else
		puts("1");#include<stdio.h>
int main()
{
	int m;
	int a[10000];
	while(scanf("%d",&m),m)
	{
		int ans=0;
		for(int i=0;i<m;i++)
		{
			scanf("%d",&a[i]);
			ans=ans^a[i];
		}
		if(ans==0)
			puts("0");//输
		else
		{
			int count=0;
			for(int i=0;i<m;i++)
			{
				if(a[i]-(ans^a[i])>0)//ans^a[i]得到的是除a[i]外所有堆的亦或结果 
					count++;
			}
			printf("%d\n",count);
		}
	}
	return 0;
}威佐夫博弈练习题 HDU1527
取石子游戏
AC代码:
#include<stdio.h>
#include<math.h>
const double eqa=(1+sqrt(5.0))/2.0;
int main()
{
	int a,b;
	while(~scanf("%d%d",&a,&b))
	{
		if(a>b)
		{
			a=a^b;
			b=a^b;
			a=a^b;
		}
		int k=b-a;
		if(a==(int)(k*eqa))
			puts("0");//遇到奇异局势,必败
		else
			puts("1");
	}
	return 0;
} 
 










