[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();
}
原文地址:https://www.cnblogs.com/C202044zxy/p/15788800.html