[AGC048D] Pocky Game
一、题目
二、解法
由于这是一个不平等博弈,我称左边的玩家为"左手",右边的玩家为"右手"
通过手玩可以给出一些合理的猜测:当前人正在拿的那一堆的石子数越多越优。证明方法同样简洁:对于石子数更少的必胜方案,我们只需要把最后一步从取最后一个石子改成取走整堆石子,其他地方都不变即可。
那么可以根据这个性质来 \(dp\),设 \(F[l][r]\) 表示 \((l,r]\) 的石子都是原序列中的整堆,左手必胜需要最小的石子数是多少;再设 \(G[l][r]\) 表示 \([l,r)\) 的石子都是原序列中的整堆,右手必胜需要的最小石子数是多少,下面我们讨论 \(F[l][r]\) 的转移,\(G[l][r]\) 的转移可以类似地推导出来。
首先考虑有两个显然的偏序关系(都是根据定义而来的):
- \(G[l+1][r]>a_r\),若此时左手执,则左手取完这堆石子之后必胜。
- \(F[l][r-1]>a_l\),若此时右手执,则右手取完这堆石子之后必胜。
第一种转移:若 \(G[l+1][r]>a_r\),那么 \(F[l][r]=1\)
第二种转移:若 \(G[l+1][r]\leq a_r\),右手一定会在 \(G[l+1][r]=a_r\) 的时候取完这堆石子,那么胜利的条件是可以逼迫后手进行完拉锯战,并且右手取完石子之后还可以满足子问题的胜利条件:
\[F[l][r]=(a_r-G[l+1][r]+1)+F[l][r-1]
\]
用区间 \(dp\) 的方式转移即可,最后判断 \(F[1][n]\) 和 \(a_1\) 的关系即可得出答案,时间复杂度 \(O(n^2)\)
三、总结
博弈论的新奇结论:考虑是否存在某一个分界点,使得它一边是 \(N\) 状态,一边是 \(P\) 状态。
博弈论中多考虑一些必胜态与必败态也许有助于思考(怎么感觉像是废话啊)
#include <cstdio>
const int M = 105;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int T,n,a[M],F[M][M],G[M][M];
void work()
{
n=read();
for(int i=1;i<=n;i++)
a[i]=read(),F[i][i]=G[i][i]=1;
for(int l=n;l>=1;l--)
for(int r=l+1;r<=n;r++)
{
//transform of F
if(G[l+1][r]>a[r]) F[l][r]=1;
else F[l][r]=(a[r]-G[l+1][r]+1)+F[l][r-1];
//transform of G
if(F[l][r-1]>a[l]) G[l][r]=1;
else G[l][r]=(a[l]-F[l][r-1]+1)+G[l+1][r];
}
puts(F[1][n]<=a[1]?"First":"Second");
}
signed main()
{
T=read();
while(T--) work();
}