AGC048D Pocky Game
若干堆石子排成一行,两个人轮流操作,先手可以操作最左边的,后手可以操作最右边的,每次可以在一堆石子中取走至少一个石子。最后不能操作的人算输。
问谁胜。
\(T\le 100,n\le 100\)
显然,一个人当前操作的堆中,石子数越多,他越有可能胜利。
于是每个人操作只会有两种情况:将整堆取完;取一个石子续命。
设\(f_{l,r,0}\)表示:操作区间\([l,r]\),轮到先手操作,此时\([l+1,r]\)中石子没有动过,求使得先手必胜的最小的\(a_l\)是多少。再设\(f_{l,r,1}\)表示类似的含义。
分类讨论(以\(f_{l,r,0}\)为例):
- 若取完整堆必胜,即\(f_{l+1,r,1}>a_r\),则取整堆,返回\(1\)。
- 否则即\(f_{l+1,r,1}\le a_r\)。先手取一个轮到后手,此时再讨论:
- \(f_{l-1,r,0}>a_l-1\),则取整堆,返回\(a_l+1\)。
- \(f_{l-1,r,1}\le a_l-1\)。到了这里我们得到了两个不等式,后面的操作就是\(a_r\)和\(a_l\)轮流减一,直到其中一个不等式被破坏。设先手必胜时\(a_l\)为\(x\),则有\(x-1-f_{l,r-1,0}\ge a_r-f_{l+1,r,1}\),这样就可以得到\(x\)的最小值。
这样就可以在\(O(n^2)\)时间内解决这题。
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 105
int n;
int a[N];
int f[N][N][2];
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int T;
scanf("%d",&T);
while (T--){
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
for (int i=n;i>=1;--i){
f[i][i][0]=f[i][i][1]=1;
for (int j=i+1;j<=n;++j){
if (a[i]==1)
f[i][j][0]=(f[i+1][j][1]>a[j]?1:a[i]+1);
else
f[i][j][0]=(f[i+1][j][1]>a[j]?1:f[i][j-1][0]>a[i]-1?a[i]+1:a[j]-f[i+1][j][1]+f[i][j-1][0]+1);
if (a[j]==1)
f[i][j][1]=(f[i][j-1][0]>a[i]?1:a[j]+1);
else
f[i][j][1]=(f[i][j-1][0]>a[i]?1:f[i+1][j][1]>a[j]-1?a[j]+1:a[i]-f[i][j-1][0]+f[i+1][j][1]+1);
}
}
// for (int i=1;i<=n;++i){
// for (int j=1;j<=n;++j)
// printf("(%d,%d) ",f[i][j][0],f[i][j][1]);
// printf("\n");
// }
if (f[1][n][0]<=a[1])
printf("First\n");
else
printf("Second\n");
}
return 0;
}