【学习笔记】[AGC048D] Pocky Game

这是一个非平等博弈。但是只要求你判断胜负,本身也不是一道结论题,所以可以用 D P DP DP来解决。

结论:第一堆石子剩的越多,先手玩家获胜的概率越大。这直接引出了一个非常感性的结论:每次取石子时要么取一堆,要么只取一个。很难理性证明这个博弈策略是正确的,但是博弈本身就是很玄学的东西,似乎我们找不出来一套普适的理论去判断游戏的胜负。那么只要这个策略本身具有合理性就可以采纳。就这道题而言,取一堆石子可以看成是加快游戏进度,取一个石子可以看成是让游戏的步数延长。看来这道题当中游戏步数是非常重要的维度,我们可以通过比较游戏步数的大小来判定胜负。

然后就是编 D P DP DP状态。设 f l , r f_{l,r} fl,r表示剩 [ l + 1 , r ] [l+1,r] [l+1,r]堆中石子时先手获胜, a l a_l al的最小数目, g l , r g_{l,r} gl,r表示剩 [ l , r − 1 ] [l,r-1] [l,r1]堆中石子时后手获胜(后手先操作), a r a_r ar的最小数目。注意,这里我们要求 [ l , r ] [l,r] [l,r]中的石子堆都非空。这个状态给我一种 border \text{border} border的感觉,也就是要么左端点被截断或者右端点被截断,正好就是对应左右两端中两堆石子被消耗的过程。

接着编具体的转移。其实并不复杂:如果 g l + 1 , r > a r g_{l+1,r}>a_r gl+1,r>ar那么直接将第 l l l堆取空就行,有 f l , r = 1 f_{l,r}=1 fl,r=1;否则先手一定是消耗,并且 a l > 1 a_l>1 al>1,任意时刻如果 a l < f l , r − 1 a_lal<fl,r1那么后手就会将第 r r r堆取完,从而先手必败,那么分类讨论:

1.1 1.1 1.1 如果 g l + 1 , r = 1 g_{l+1,r}=1 gl+1,r=1,那么一定要是后手取完,并且此时 a l a_l al恰好为 f l , r − 1 f_{l,r-1} fl,r1,有 f l , r = f l , r − 1 + a r f_{l,r}=f_{l,r-1}+a_r fl,r=fl,r1+ar

1.2 1.2 1.2 如果 g l + 1 , r ≠ 1 g_{l+1,r}\ne 1 gl+1,r=1,那么当 a l = f l , r − 1 a_l=f_{l,r-1} al=fl,r1时第 r r r堆也恰好为 g l + 1 , r − 1 g_{l+1,r}-1 gl+1,r1,此时再将 a l a_l al取完就变成先手必胜了,有 f l , r = f l , r − 1 + a r − g l + 1 , r + 1 f_{l,r}=f_{l,r-1}+a_r-g_{l+1,r}+1 fl,r=fl,r1+argl+1,r+1

后手和先手是对称的就不说了。这个 D P DP DP转移还挺容易推错的,可能主要是因为没有想到临界时两端的石子数目都不为 0 0 0

复杂度 O ( n 2 ) O(n^2) O(n2)

#include
#define ll long long
#define fi first
#define se second
#define pb push_back
#define db double
using namespace std;
int T,n;
ll a[105],f[105][105],g[105][105];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>T;
    while(T--){
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i];
        memset(f,0x3f,sizeof f),memset(g,0x3f,sizeof g);
        for(int i=1;i<=n;i++){
            f[i][i]=1,g[i][i]=1;
        }
        for(int len=2;len<=n;len++){
            for(int l=1;l<=n-len+1;l++){
                int r=l+len-1;
                if(g[l+1][r]>a[r])f[l][r]=1;
                else f[l][r]=f[l][r-1]+a[r]-g[l+1][r]+1;
                if(f[l][r-1]>a[l])g[l][r]=1;
                else g[l][r]=g[l+1][r]+a[l]-f[l][r-1]+1;
            }
        }
        if(n==1||f[1][n]<=a[1]){
            cout<<"First"<<"\n";
        }
        else{
            cout<<"Second"<<"\n";
        }
    }
}

你可能感兴趣的:(学习,笔记)