组合游戏定义:
1、有且仅有两个玩家
2、游戏双方轮流操作
3、游戏操作状态是个有限的集合(比如:取石子游戏,石子是有限的,棋盘中的棋盘大小的有限的)
4、游戏必须在有限次内结束
5、当一方无法操作时,游戏结束。
(一)巴什博奕
1.游戏规则:有一堆n个物品,两人轮流从堆中取物品,每次取 x 个 ( 1 ≤ x ≤ m)。最后取光者为胜。
2.分析:由于一次至多取 m 个,当 n = m + 1时,如果先取者取了x个(1<=x<=m),后取者一定能取走n-x个(1<=n-x<=n),即能够一次拿走剩余的物品,此时,后取者必胜。因此,我们可以发现取胜的法则,当n=(m+1)*k+r(k为任意自然数,r<=m),此时,先取者第一次拿走r个物品,如果后取者拿走x(<=m)个,先取者只要拿走m+1-x个,保证剩下的为(m+1)的倍数,那么先取者必胜。现在,我们可以知道,如果 s = 0,那么后取者必胜,否则先取者必胜。
对于巴什博奕,如果我们规定最后取关者失败,则需要满足(n-1)%(m+1)等于0。
3.对于这类题,一般可以画出PN图,根据规律直接求解。
4.相关题目:(都是水题)
hdu 1846 Brave Game 点击打开链接
代码实现如下:
#include
int main()
{
int t,n,m;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
if(n%(m+1)==0)
printf("second\n");
else printf("first\n");
}
}
hdu 2516 取石子游戏 点击打开链接
一个典型的斐波拉契博弈,在网上看到了一篇对斐波拉契博弈原理讲的很清楚的博文,可以参考一下:http://blog.csdn.net/dgq8211/article/details/7602807
代码实现如下:
#include
int main()
{
int f[48];//n趋于48时,f[n]趋于int的最大值
f[0] = 0;
f[1] = 1;
for(int i=2;i<48;i++)
{
f[i]=f[i-1]+f[i-2];
}
int n;
while(~scanf("%d",&n)&&n)
{
int flag=0;
for(int i=3;i<48;i++)
if(n==f[i])
{
flag=1;
break;
}
if(flag)
printf("Second win\n");
else
printf("First win\n");
}
}
hdu 2149 Public Sale 点击打开链接
代码实现如下:
#include
int main()
{
int m,n;
int a[105];
while(~scanf("%d%d",&m,&n))
{
if(m%(n+1)==0)//先手必败
printf("none\n");
else
{
if(n>=m)//当n>=m时,无论lele叫价多少都可以获得这块地
{
for(int i=m;i<=n;i++)
if(i==n)printf("%d\n",i);
else printf("%d ",i);
}
else
{
int flag=0;//只是保证输出的末尾没有空格
for(int i=1;i<=n;i++)
if((m-i)%(n+1)==0)//留下n+1的倍数才能保证必胜
{
if(!flag)
printf("%d",i);
else printf(" %d",i);
flag=1;
}
printf("\n");
}
}
}
}
hdu 2147 kiki's game 点击打开链接
画出PN图
P | N | P | N | P |
N | N | N | N | N |
P | N | P | N | P |
N | N | N | N | N |
P | N | P | N | P |
由图可以看出,当m或者n有一个是偶数时,先手就能必胜。
代码实现如下:
#include
int main()
{
int n,m;
while(~scanf("%d %d",&n,&m))
{
if(n==0 && m==0)break;
if(n%2==0 || m%2==0)
printf("Wonderful!\n");
else
printf("What a pity!\n");
}
return 0;
}
(二)威佐夫博弈
1.游戏规则:有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
2.分析:
poj 1067 取石子游戏 点击打开链接
代码实现如下:
#include
#include
int main()
{
int a,b;
while(~scanf("%d%d",&a,&b))
{
int m=abs(a-b);
a=a
代码实现如下:
#include
#include
#include
using namespace std;
int main()
{
int a,b,temp,temp2,k,i, tmp;
while(scanf("%d%d",&a,&b),a+b)
{
if(a>b)
swap(a,b);
k=b-a;
temp=k*(1.0+sqrt(5.0))/2.0;
if(a==temp)//先手必败
printf("0\n");
else
{
printf("1\n");
if(abs(temp-a)==abs(temp+k-b)&&temp
(三)尼姆博弈
1.游戏规则:有任意堆物品,每堆物品的个数是任意的,双方轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取一件,取到最后一件物品的人获 胜。
2.分析:这个问题分析起来是比较复杂的,在网上看到了一篇讲的很棒的博文,顺便偷个懒,有空再自己写写。附上原博文的网址点击打开链接
3.相关题目:
POJ 2234 Matches Game 点击打开链接
一道最基础的尼姆博弈题。只需要简单异或就行了,最后判断是否为零,若是零就是后手必胜,反之先手。
代码实现如下:
#include
int main()
{
int c;
while(~scanf("%d",&c))
{
int n,sum=0;
for(int i=0;i
代码实现如下:
#include
int main()
{
int m;
int a[200005];
while(~scanf("%d",&m)&&m)
{
int ans=0;
for(int i=0;i(ans^a[i]))//我们可以知道,对任意的当前状态S,只需将某堆石头a[i]变为ans^a[i]即可使得整个局面的SG值为0,即变为必败态,所以只需要a[i]比ans^a[i]大即为合理状态。
printf("%d %d\n",a[i],ans^a[i]);
}
}
}
}
nim博弈进阶——阶梯博弈
1.游戏规则:在一列阶梯上进行博弈,每个阶梯上放着若干个点,两个人进行博弈,每一步则是将一个阶梯上的若干个点( 大于等于一个 )移到前面的阶梯上去,最后无法移动的人输。
2.分析:【转自网络点击打开链接】阶梯博弈可以转换为奇数位置上的nim博弈。原因如下:假设我们是先手,所给的阶梯石子状态的奇数堆做Nim先手能必胜。按照能赢的步骤将奇数堆的石子移动到偶数堆...如果对手也是移动奇数堆..我们继续移动奇数堆..如果对手将偶数堆的石子移动到了奇数堆..那么我们紧接着将对手所移动的这么多石子从那个奇数堆移动到下面的偶数堆...两次操作后...相当于偶数堆的石子向下移动了几个..而奇数堆依然是原来的样子...即为必胜的状态...就算后手一直在移动偶数堆的石子到奇数堆..我们就一直跟着他将石子继续往下移..保持奇数堆不变...如此做下去..我可以跟着后手把偶数堆的石子移动到0..然后你就不能移动这些石子了...所以整个过程..将偶数堆移动到奇数堆不会影响奇数堆做Nim博弈的过程..整个过程可以抽象为奇数堆的Nim博弈...其他的情况...先手必输的...类似推理...只要判断奇数堆做Nim博弈的情况即可...为什么是只对奇数堆做Nim就可以...而不是偶数堆呢?因为如果是对偶数堆做Nim...对手移动奇数堆的石子到偶数堆..我们跟着移动这些石子到下一个奇数堆...那么最后是对手把这些石子移动到了0..我们不能继续跟着移动...就只能去破坏原有的Nim而导致胜负关系的不确定...所以只要对奇数堆做Nim判断即可知道胜负情况。