基础博弈论(巴什博弈、威佐夫博弈、尼姆博弈)(未完待续)

版权声明:欢迎转载,转载请标明作者和出处。 https://blog.csdn.net/ljw_study_in_CSDN/article/details/88356973

先给出三大博弈的定义和基本做法。

巴什博弈:只有一堆共n个石子,两人轮流取,最少取一个,最多取m个,最先取光石子的人赢。
做法:求n%(m+1),如果等于0先手必输,不等于0则先手必赢。

威佐夫博弈:有两堆各x,y个石子,两人轮流从某一堆或同时从两堆中取同样多的石子,规定每次至少取一个,多者不限,最先取光石子的人赢。
做法:若两堆物品的初始值为(x,y),且x< y,记w=(int)((sqrt(5.0)+1.0)/2.0∗(y-x));如果w=x则先手必输,w!=x则先手必胜。

尼姆博弈:有n堆石子(可假设分别为a[1]~a[n]个石子),两人轮流从某一堆取任意多的石子,规定每次至少取一个,多者不限,最先取光石子的人赢。
做法:将所有堆的石子异或起来,如果等于0则先手必输,不等于0则先手必赢。

其他概念:先手必输为P-position,先手必赢为N-position

  • HDU2147 巴什博弈
  • 博弈论:组合博弈
  • 必败点(P点) :前一个选手(Previous player)将取胜的位置称为必败点。
  • 必胜点(N点) :下一个选手(Next player)将取胜的位置称为必胜点。
  • 现在关于P,N的求解有三个规则
    (1):最终态都是P
    (2):按照游戏规则,到达当前态的前态都是N的话,当前态是P
    (3):按照游戏规则,到达当前态的前态至少有一个P的话,当前态是N

hdu 1846 Brave Game
巴什博弈模板题

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n,m,t;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        if(n%(m+1)==0)printf("second\n");
        else printf("first\n");
    }
    return 0;
}

poj 1067 取石子游戏
威佐夫博弈模板题

#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
int main()
{
    int x,y,w;
    ios::sync_with_stdio(false);
    while(cin>>x>>y)
    {
        w=(int)((sqrt(5.0)+1.0)/2.0*fabs(y-x));
        if(w==(min(x,y)))printf("0\n");
        else printf("1\n");
    }
    return 0;
}

poj 2234 Matches Game
尼姆博弈模板题

#include <cstdio>
#include <iostream>
using namespace std;
int main()
{
    int m,x,ans;
    while(cin>>m)
    {
        ans=0;
        while(m--)
        {cin>>x;ans=ans^x;}
        if(ans==0)printf("No\n");//所有石子异或为0,先手必输
        else printf("Yes\n");
    }
    return 0;
}

poj 2975 Nim
尼姆博弈。
给定nim游戏的一种局面,你是A,对手是B,问你现在这一步有多少种取法使得你进入必胜态。
先求出原状态下所有石子的异或,设为tmp。判断tmp,如果tmp=0,你开始取石子,相当于你是先手,必输,即获胜的方法数为0,直接输出0即可。
如果tmp!=0,那么在你取了之后,就是B取,可以看成你取了石子(必须至少取一个)改变状态后,才正式开始游戏,这时可以把B看作先手,B开始在你取了石子之后的新状态下开始取。要使在你取了石子后的新状态下,作为先手的B必输,也就是要上一步你取了石子之后,所有石子异或为0,此时B必输,A必赢。
遍历a[i],上一步你只能选择一堆石子a[i]取走任意个石子,除a[i]以外的所有石子异或 tmp^a[i] 不变。要在a[i]中取走任意个石子后B必输,也就是要保证上一步你取了石子之后所有石子异或为0,也就是要在a[i]中取任意个石子后剩下的数量为tmp^a[i],那么只要 tmp^a[i]<a[i] 即可满足条件。

#include <cstdio>
#include <iostream>
using namespace std;
int main()
{
    int n,i,tmp,cnt,a[1001];
    ios::sync_with_stdio(false);
    while(cin>>n&&n)
    {
        tmp=cnt=0;
        for(i=1;i<=n;i++)
        {cin>>a[i];tmp=tmp^a[i];}
        if(tmp==0){printf("0\n");continue;}//tmp=0说明在你取石子之前所有石子异或为0,此状态先手必输,这时你是先手,必输
        for(i=1;i<=n;i++)
        {if((tmp^a[i])<a[i])cnt++;}//你已经取了石子,并且保证了(tmp^a[i])<a[i],则你取石子后先手的B必输
        //if(tmp^a[i]<a[i])是错的,没在tmp^a[i]外写括号则会先计算<符号再计算^符号异或优先级低于关系运算符
        printf("%d\n",cnt);
    }
    return 0;
}

poj 2484 A Funny Game
博弈,从进入对称状态入手。
当n<=2时,显然A必胜;当n>=3时,A先手取走一个或两个硬币后,由于硬币组成的环被截断,把环拉直,相当于环被切断后变成了"一条绳子",把"绳子"看成一段序列,那么B则根据这段序列长的奇偶性取出序列最中间的一个或两个硬币,从而把"这条绳子"再切成"两条对称的绳子",这时进入了对称状态,A开始取,由于被切断后的"两条绳子"对称,那么以后的每一步,B只要模仿A取硬币的方式,A取几个硬币,B就可以在对称位置上取几个硬币,这样就可以保证B总会取到最后一个硬币,B必胜。实际上,进入对称状态,就相当于有两堆一样多的石子,A先手开始取,根据尼姆博弈,两堆石子异或为0(因为两堆石子个数一样),所以A必败。

#include <cstdio>
#include <iostream>
using namespace std;
int main()
{
    int n;
    while(cin>>n&&n)
    {
        if(n<=2)printf("Alice\n");
        else printf("Bob\n");
    }
    return 0;
}

参考资料:
1.尼姆博弈 https://blog.csdn.net/k_koris/article/details/81743806

猜你喜欢

转载自blog.csdn.net/ljw_study_in_CSDN/article/details/88356973