三个博弈-巴什博奕、威佐夫博弈、尼姆博弈。acm博弈算法笔记HDU 2149,1850,1527

博弈论

(一)、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
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,这就给了对手有必赢策略的机会


代码:

#include
int main()
{
	int m;
	int a[10000];
	while(scanf("%d",&m),m)
	{
		int ans=0;
		for(int i=0;i0)//ans^a[i]得到的是除a[i]外所有堆的亦或结果 
					count++;
			}
			printf("%d\n",count);
		}
	}
	return 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
#include
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

也可以判断任意给出的(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");



威佐夫博弈练习题 HDU1527

取石子游戏

AC代码:

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




你可能感兴趣的:(ACM**博弈*******)