一般规律的博弈

http://acm.hdu.edu.cn/showproblem.php?pid=1525

题意:给你两个数,每次都是大数减去小数的整数倍,Stan先开始,然后是Ollie,如果谁先出现 0,b 这种情况谁就获胜了。

分析:1.当a=b时,这时候先手必胜。
2.当a>b时,这时候有两种情况
①a%b=0,此时a为b的倍数,先手必胜。
②a>=2b,此时先手可以进行选择,进行什么选择呢?每个人都想获胜,所以他们采取的都是最利于自己的行动,Stan或者Ollie肯定知道 a%b,b 是必胜态还是必败态。如果是必败态,先手将a,b变成a%b,b,使对手面临必败态,先手赢。如果是必胜态,先手将a,b变成 a%b+b,b ,破坏掉必胜态,那么对手只有将这两个数变成 a%b,b ,先手获胜。( 25,7 这个例子就能说明②这种情况 )

综上 关键是找到必胜态
a>b 与 b>a 情况一致
即a==b||a%b==0||a>=2*b时先手胜
否则就一步一步走,看到最后谁胜利。( 15,24这个例子能说明这种情况)

#include
#include
using namespace std;
int main()
{
    long long a,b;
    while(scanf("%lld%lld",&a,&b)!=EOF,a+b)
    {
        if(a=2*b)break;
            a=a-b;
            if(a

http://acm.hdu.edu.cn/showproblem.php?pid=1564
题意:一个nn的棋盘,一开始的时候棋子在一个角落的格子里,每次可以移动棋子到上下左右的相邻格子中(不能超过边界,而且不能走走过的格子);当不能走时,败。现在8600先走,问最后谁赢。
题解:这道题赢家的关键就是最先抢夺过程中剩余可以走的个数为奇数的状态,总的个数为n
n刚开始可以走的个数为nn-1,如果n为偶数nn-1为奇数,则第一个走的人面临的就是必赢状态,否则第二走的人会赢。题解来源

#include
int main()
{
    int n;
    while(~scanf("%d",&n),n)
    {
       if(n&1) printf("ailyanlu\n");
       else printf("8600\n");
    }
    return 0;
}

对称博弈
题目大意:给你n个硬币,把它围成一个圆圈。现在有两个人玩这样的一个翻转游戏,每次翻转1--k个硬币,最后一个翻转硬币者胜。
题解:1) 若k=1,则一次只能去翻一枚,奇数先手赢,偶数后手赢。
2)若k>1:
a: 先手一次翻完,先手赢;
b: 先手不能翻完,第一次必定断环。只要后手一次翻完,或将其分为相等数量的两段,
之后先手怎么操作后手就怎么操作,后手必赢。

#include
#include
using namespace std;
int main()
{
    int t;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        if(k==1)
        {
            if(n&1) printf("Case %d: first\n",cas);
            else printf("Case %d: second\n",cas);
        }
        else {
            if(k>=n) printf("Case %d: first\n",cas);
            else printf("Case %d: second\n",cas);
        }
    }
    return 0;
}

阶梯博弈
原作者的阶梯博弈详解
首先是对阶梯博弈的阐述...博弈在一列阶梯上进行...每个阶梯上放着自然数个点..两个人进行阶梯博弈...每一步则是将一个集体上的若干个点( >=1 )移到前面去..最后没有点可以移动的人输..

一般规律的博弈_第1张图片

如这就是一个阶梯博弈的初始状态 2 1 3 2 4 ... 只能把后面的点往前面放...如何来分析这个问题呢...其实阶梯博弈经过转换可以变为Nim..把所有奇数阶梯看成N堆石子..做nim..把石子从奇数堆移动到偶数堆可以理解为拿走石子..就相当于几个奇数堆的石子在做Nim..( 如所给样例..234=5 不为零所以先手必败)为什么可以这样来转化?
假设我们是先手...所给的阶梯石子状态的奇数堆做Nim先手能必胜...我就按照能赢的步骤将奇数堆的石子移动到偶数堆...如果对手也是移动奇数堆..我们继续移动奇数堆..如果对手将偶数堆的石子移动到了奇数堆..那么我们紧接着将对手所移动的这么多石子从那个奇数堆移动到下面的偶数堆...两次操作后...相当于偶数堆的石子向下移动了几个..而奇数堆依然是原来的样子...即为必胜的状态...就算后手一直在移动偶数堆的石子到奇数堆..我们就一直跟着他将石子继续往下移..保持奇数堆不变...如此做下去..我可以跟着后手把偶数堆的石子移动到0..然后你就不能移动这些石子了...所以整个过程..将偶数堆移动到奇数堆不会影响奇数堆做Nim博弈的过程..整个过程可以抽象为奇数堆的Nim博弈...
其他的情况...先手必输的...类似推理...只要判断奇数堆做Nim博弈的情况即可...
为什么是只对奇数堆做Nim就可以...而不是偶数堆呢?...因为如果是对偶数堆做Nim...对手移动奇数堆的石子到偶数堆..我们跟着移动这些石子到下一个奇数堆...那么最后是对手把这些石子移动到了0..我们不能继续跟着移动...就只能去破坏原有的Nim而导致胜负关系的不确定...所以只要对奇数堆做Nim判断即可知道胜负情况...

https://vjudge.net/problem/POJ-1704
http://blog.sina.com.cn/s/blog_6a6aa7830100p4nb.html
题意:给定n个不同的数表示n个位置上有棋子,两个人轮流进行操作,不能操作的人输,每次可以选一个棋子然后向左移动至少一步并且不能越过别的棋子并且一个格子最多只能有一个棋子。判断先手能否必胜。
题解:由于只能向左移,而且不能相互交叉。我们把两个硬币之间的空间看作石子堆,一次左移相当于把某个石子堆(楼梯j)的石子移到了右边那个石子堆(楼梯j-1),最后的区间相当于最低的一层,题目实质就变成了阶梯博弈。

#include 
#include
using namespace std;
int arr[1010];
int main()
{
    int t,n,k,res;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        arr[0]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",arr+i);
        }
        sort(arr+1,arr+n+1);
        if(n&1) k=1;
        else k=2;
        res=0;
        for(;k<=n;k+=2)
        {
            res^=(arr[k]-arr[k-1]-1);
        }
        if(res) printf("Georgia will win\n");
        else printf("Bob will win\n");
    }
}

http://acm.hdu.edu.cn/showproblem.php?pid=4315
题意:有一群人在爬山,每个人有个离山顶的距离,且没有两个人在同一位置,可以多个人在山顶,其中有一个国王在位置k(标号),两个人轮流操作:任意选一个人然后将他向上移动至少位置1且不能越过其他人,谁将国王移动到山顶谁获胜。
题解:首先考虑没有king的情况,也就是说没有合法操作的那个人判负。这样的话,可以发现和上一道题的阶梯博弈是等效的。只需要将区间的距离看成是一堆一堆的石子,然后将编号为奇数的堆异或起来就行了。 但是有一个比较特殊的地方,就是king需要特判一下。当一共有奇数个人并且king在第二个人时,需要将第一个区间的长度-1,因为如果将第一个区间直接移动到山顶的话其实是一个必胜态,只有将第一个区间留下才是必败态。并且如果king在第一个人的话Alice必胜。

#include 
#include
using namespace std;
int arr[1010];
int main()
{
    int n,res,k;
   while(scanf("%d%d",&n,&k)!=EOF)
   {
      for(int i=1;i<=n;i++)
      {
          scanf("%d",arr+i);
      }
      if(k==1) {
        printf("Alice\n");
        continue;
      }
      res=0;
      if(k==2&&n&1) arr[0]=0;
      else arr[0]=-1;
      for(int i=n;i>=1;i-=2)
      {
          res^=(arr[i]-arr[i-1]-1);
      }
      if(res) printf("Alice\n");
      else printf("Bob\n");
   }
}

http://acm.hdu.edu.cn/showproblem.php?pid=3389
阶梯博弈:
题意:有t组数据。每组数据有n个盒子,这n个盒子编号为12345678......。(注意不是从0开始的)每个盒子中有一定量的卡片。每次取编号为B和编号为A的盒子, 要求满足B

题解:
1 3 4号盒子是不能够再转移卡片到其他盒子中去了的,其他盒子中的卡片经过若干步的转移最终也一定会转移到1 3 4号盒子中去。所以是阶梯博弈。阶梯博弈只需要考虑步数为奇数的盒子,步数为偶数的盒子不需要考虑。在本题中编号为1,3,4的盒子不能转移卡片,其余盒子均可转移。例如:2->1, 5->4, 6->3, 7->2 ,8->1, 9->6...其本质为有n级阶梯,我们在%3的余数中进行转移0->0, 1->2, 2->1;最后的结果一定是1或者3或者4,这些盒子中卡片转移的步数的奇偶性是一定的。为什么这么说呢?因为即使有些盒子例如编号11的盒子,有11->4和11->10->8->1两种选择,但是这两种选择的步数的奇偶性是相同的,都是奇数,所以奇偶性是一定的。所以我们把这个阶梯博弈转化为尼姆博弈就行了,对步数为奇数的盒子进行尼姆博弈。可以发现如下规律:盒子编号模6为0,2,5的位置的移动步数为奇,其余为偶。
原作者题解

#include 
int main()
{
    int t;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++)
    {
        int n,tmp,res=0,mod;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&tmp);
            mod=i%6;
            if(mod==0||mod==2||mod==5) res^=tmp;
        }
        if(res) printf("Case %d: Alice\n",cas);
        else printf("Case %d: Bob\n",cas);
    }
    return 0;
}

海盗分金问题
原博主博客
有n个海盗,分m块金子,其中他们会按一定的顺序提出自己的分配方案,如果不少于50%的人赞成(包括他自己),则方案通过,开始分金子,如果不通过,则把提出方案的扔到海里,下一个人继续。编号为n的人首先做决策,求位置p的海盗能分到的金子数量。如果p被扔到海里,输出"Thrown".

题解:
首先我们讲一下海盗分金决策的三个标准:保命,拿更多的金子,杀人,优先级是递减的。
同时分为两个状态稳定状态和不稳定状态:如果当n和m的组合使得最先决策的人(编号为n)不会被丢下海, 即游戏会立即结束, 就称这个状态时"稳定的". 反之, 问题会退化为n-1和m的组合, 直到达到一个稳定状态, 所以乘这种状态为"不稳定的".

接下来我们从简单的开始分析:
如果只有两个人的话:那么2号开始提出方案,这时候知道不管提什么,他自己肯定赞成,过半数,方案通过,那么2号肯定把所有的金子都给了自己。
如果只有三个人的话:那么3号知道,如果自己死了,那么2号肯定能把所有金子拿下,对于1号来说没有半点好处。
那么他就拿出金子贿赂1号,1号拿到1个金子,总比没有好,肯定赞成3号,剩下的3号拿下。
如果只有四个人的话:那么4号知道,如果自己死了,那么1号拿到1个金子,2号什么都没有,3号拿下剩下的金子。
那他就可以拿出部分金子贿赂2号,2号知道如果4号死了,自己将什么都没有,他肯定赞成4号。
如此类推下去,貌似就是第一个决策的时候,与他奇偶性相同的人会被贿赂拿到1个金子,剩下的全归提出方案的人所有。

但是会有一个问题便是,如果金子不够贿赂怎么办。
情况1、我们首先归纳之前的,如果n<=2m时候,前面与n相同奇偶性的得到1个金子,剩下的第n个人拿下。
情况2、如果n==2
m+1,第n个人拿出m个金子贿赂前面的m个人。自己不拿金子,这样刚好保证自己不死,这就是之前提到的优先级,首先得保命,如果自己拿了一个金子,那么前面就有一个人会反对,因为对于那个人,不管怎么样都分不到金子,则轮到第三个原则,杀人,肯定投反对票。

剩下来我们考虑,钱不够贿赂的情况:
我们将问题具体化:如果有500个海盗,只有100个金子,那么前面201个已经分析过了。
对于202号来说,自己不能拿金币,而贿赂上一轮没有拿到金币的101人中的100人就够了。
对于203号来说,需要102个人的支持,显然加上他自己,还需要101票,而金子不够贿赂,别人会反对,而达到杀人的目的。
对于204号来说,他知道一旦自己死了,203号是必死,抓住这点,203必然支持他,因为203号宁可不要金币,也要保住性命,所以204号把100个金币分给之前的100个人,然后203和他自己的两票保证自己不死。
对于205号来说,203,和204是不会支持他的,因为一旦205死了,他们不仅可以保住性命,而且还可以看着205死掉。所以205是必死
那么206呢,虽然205必死,会支持他,但是还是缺一票,所以必死。
对于207呢,205和206之前是必死,会支持他,但是加上自己以及100个贿赂名额,还是必死
对于208号,205,206.,207因为后面是必死的,肯定会支持208成功,那么208刚好能凑齐104票,得以保命。

以下我们猜想:n=2m+2^k的情况下,是可以保命的,称为稳定状态,否则为不稳定状态,我们证明一下:
首先对于n来说,有m票贿赂,但是对于2
m+2(k-1)以前必死的死,他们会支持2*m+2(k-1),因为他们肯定拿不到钱,而且支持2m+2^(k-1),另外根据杀人原则,希望之后的人都死,轮到2m+2^(k-1)决策的时候保命就行了。
同理2m+2^(k-1)到2m+2k之间的2(k-1)-1个人来说,他们必死,所以必定支持2m+2k,加上m个金币贿赂的,加上他自己,刚好有m+2(k-1)。这样刚好凑齐一半,可以不死。
证明完毕:2
m+2^k的人可以保命,否则必死。

我们考虑一下分金币情况:
情况3:对于第2m+2^k个人来说,他可以保命,肯定分不到金子,而他手上的m个金子,可以贿赂m个人,但是具体是哪些人是不定的。则不管是不能分到金子,还是可能分不到金子的人来说,结果都为0。
情况4:对于2
m+2(k-1)到2*m+2k之间的来说,他们的决策是必死,而在他们决策的时候,其它人分得金币情况也为0。

我们来解释一下金币的不确定性:
金币数量的不确定性:由上面的推理可知, 当n=2m+2时, 上一轮推理没有分到金币的人的金币数量首次具有不确定性, 并且在n>2m+2时, 这种不确定性一定会延续下去, 轮到因为n号决策者之前的一个人决策时, 那个人肯定分不到金币了, 所以在上一轮推理中没有分到金币的人的个数一定大于m.

综合情况1,2,3,4便是本题的解。

#include 
#include 
#include 
#include 
using namespace std;
int fac[15]={2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};//保存2的幂 
void solve(int n,int m,int p)
{
    //金币够贿赂的情况  
    if(n<=2*m)
    {
        //不是决策者,而且奇偶性相同,都能被贿赂  
        if(n!=p&&(p%2==n%2)) printf("1\n");
        else if(n==p) printf("%d\n",m-(n-1)/2);//剩下的都是决策者拥有
        else printf("0\n");//其余人分不到金币,他们的决策不影响全局 
        return;
    }
    else if(n==2*m+1)   //这时候的不同在于决策者不能拿金币  
    {
        if(n==p) printf("0\n");//p是决策者
        else if(p&1) printf("1\n");//p不是决策者,p的奇偶性与决策者一样,p接受决策者的贿赂
        else printf("0\n");
        return;
    }
    
    
    int t=n-2*m;
    //这是刚好保命的情况,对于决策者来说,肯定没有金币  
    //对于其它人来说,要么肯定没有金币,要么可能没有金币,不确定性  
    for(int i=1;i<14;i++)
    {
        if(t

你可能感兴趣的:(一般规律的博弈)