博弈论
(一)、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;
}