[AGC048D] Pocky Game

一、题目

点此看题

二、解法

由于这是一个不平等博弈,我称左边的玩家为"左手",右边的玩家为"右手"

通过手玩可以给出一些合理的猜测:当前人正在拿的那一堆的石子数越多越优。证明方法同样简洁:对于石子数更少的必胜方案,我们只需要把最后一步从取最后一个石子改成取走整堆石子,其他地方都不变即可。

那么可以根据这个性质来 dp,设 F[l][r] 表示 (l,r] 的石子都是原序列中的整堆,左手必胜需要最小的石子数是多少;再设 G[l][r] 表示 [l,r) 的石子都是原序列中的整堆,右手必胜需要的最小石子数是多少,下面我们讨论 F[l][r] 的转移,G[l][r] 的转移可以类似地推导出来。

首先考虑有两个显然的偏序关系(都是根据定义而来的):

  • G[l+1][r]>ar,若此时左手执,则左手取完这堆石子之后必胜。
  • F[l][r1]>al,若此时右手执,则右手取完这堆石子之后必胜。

第一种转移:若 G[l+1][r]>ar,那么 F[l][r]=1

第二种转移:若 G[l+1][r]ar,右手一定会在 G[l+1][r]=ar 的时候取完这堆石子,那么胜利的条件是可以逼迫后手进行完拉锯战,并且右手取完石子之后还可以满足子问题的胜利条件:

F[l][r]=(arG[l+1][r]+1)+F[l][r1]

用区间 dp 的方式转移即可,最后判断 F[1][n]a1 的关系即可得出答案,时间复杂度 O(n2)

三、总结

博弈论的新奇结论:考虑是否存在某一个分界点,使得它一边是 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();
}
posted @   C202044zxy  阅读(290)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
历史上的今天:
2021-01-11 [湖北省队互测2014] 一个人的数论
点击右上角即可分享
微信分享提示