AGC048D Pocky Game

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

问谁胜。

(Tle 100,nle 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("
");
//		}
		if (f[1][n][0]<=a[1])
			printf("First
");
		else
			printf("Second
");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/13955915.html