博弈论(六)——#10248. 「一本通 6.7 练习 5」取石子游戏

我怕爱的太早我们不能终老 提交于 2020-09-28 19:45:23

题目链接:https://loj.ac/problem/10248#submit_code
解题思路
神仙题目,迷一般的状态转移,看了yyb大佬的题解,才算明白一点。我们定义L[i][j]表示区间[i,j]左边(即i-1位)加上一堆石子L[i][j],能使先手必败,R[i][j]表示区间[i,j]右边(即j+1位)加上一堆石子R[i][j],能使先手必败。首先,边界L[i][i],R[i][i]肯定是a[i] (Nim博弈)。然后最后只要判断a[1]是否等于L[2][n]就能判断先手必胜还是必败了。
再看状态转移,L[i][j]由L[i][j-1]与R[i][j-1]转移过来,R[i][j]由R[i+1][j],L[i+1][j]转移过来,先看L[i][j],我们设L=L[i][j-1],R[i][j-1],x=a[j]。


  1. R=x,L[i][j]=0,因为此时已经是必败态了,不必再添了。
  2. x<L&&x<R,L[i][j]=x,此时两侧石子一致,无论先手怎么去,后手只要模仿先手在另一侧取相同的数量,这样就能保证先手先取完,此时左侧或右侧肯定有一堆没取完,此时可以等价的认为,当前是先手从L或R取了一定数量的石子转移过来的,此时无论后手怎么取都是必胜的。
  3. L<x<R,L[i][j]=x+1,若先手在左侧取,剩下石子为rest。rest=0,等价于先手在R的状态下,在右侧取了一定数量的石子转移过来。rest<L,后手只要将右侧石头取到与rest相同,就可以转移到第2种情况。rest=L,后手将右侧石子取完,就转移到L。rest>L,后手只要将右手石子取到rest-1即又回到当前情况。若先手在右侧取,当rest=0,将左侧取出L,即可转移到L。当rest<L,同上,取左侧,可以转移到第2种情况。rest>=L,后手通过在左侧或右侧,使得左侧=右侧+1,又回到当前情况。
  4. R<x<L,L[i][j]=x-1,与第三种情况类似,可以看成互换一下左右两侧。
  5. 当x>L&&x>R,L[i][j]=x。无论先手怎么取,后手都可以转移到之前和当前的状态。

R[i][j]和L[i][j]是对称的,类似的求解即可。

AC代码

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e3+5;
int a[maxn],L[maxn][maxn],R[maxn][maxn];
int T,n;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        scanf("%d",&a[i]);
        for(int i=1;i<=n;++i)
        L[i][i]=R[i][i]=a[i];
        for(int len=2;len<=n;++len)
        for(int i=1,j=i+len-1;j<=n;++i,++j)
        {
            int x=a[j],l=L[i][j-1],r=R[i][j-1];
            if(x==r)
            L[i][j]=0;
            else
            if((x>l&&x>r)||(x<l&&x<r))
            L[i][j]=x;
            else
            if(r<x&&x<l)
            L[i][j]=x-1;
            else
            L[i][j]=x+1;
            x=a[i],l=L[i+1][j],r=R[i+1][j];
            if(x==1)
            R[i][j]=0;
            else
            if((x>l&&x>r)||(x<l&&x<r))
            R[i][j]=x;
            else
            if(r<x&&x<l)
            R[i][j]=x+1;
            else
            R[i][j]=x-1;
        }
        puts(a[1]==L[2][n]?"0":"1");
    }
    return 0;
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!