首页 > 技术文章 > AGC048D Pocky Game

jz-597 2020-11-10 20:44 原文

若干堆石子排成一行,两个人轮流操作,先手可以操作最左边的,后手可以操作最右边的,每次可以在一堆石子中取走至少一个石子。最后不能操作的人算输。

问谁胜。

\(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}\)为例):

  1. 若取完整堆必胜,即\(f_{l+1,r,1}>a_r\),则取整堆,返回\(1\)
  2. 否则即\(f_{l+1,r,1}\le a_r\)。先手取一个轮到后手,此时再讨论:
    1. \(f_{l-1,r,0}>a_l-1\),则取整堆,返回\(a_l+1\)
    2. \(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;
}

推荐阅读