CF1600E Array Game

CF题目链接
洛谷链接

一道不太正经的博弈论题,可用递归解决

题意

给出一个序列,Alice与 Bob 轮流从序列两端取数,并把取出的数按顺序组成一个序列,需要保持此序列严格递增,最后不能取数的人败,求先手是否有必胜策略


定义序列 a , b a,b a,b 分别为原序列 w w w 的最长递增前缀和最长递减后缀,则第一个被取走的数为 a a a b b b 的首元素

  • 首先,存在为空的序列,那么如果非空序列长度为奇数先手必胜,否则先手必败

  • 如果首元素较大的序列的长度为奇数,那么先手必胜
    因为先手取了第一个数后,后手就只能在该序列取数。
    同理,如果两个首元素同样大,那么只要其中一个序列长度为奇数,则仍旧先手必胜

  • 否则,为了不做必败决策,两人会一直取首元素较小的序列,直到该序列首元素不再是最小

我们发现,接下来需要解决的问题是当前问题的子问题。那么我们只需要处理一下先手的变化情况,并递归求解即可

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<deque>
using namespace std;
const int Maxn=2e5+10;
deque <int> a[2];
int w[Maxn];
int n;
inline int read()
{
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return s*w;
}
int work()
{
	for(int i=0;i<2;++i)
	if(!a[i].size())return (a[i^1].size() & 1)^1;
	if(a[0][0]==a[1][0])return ((a[0].size()&1)|(a[1].size()&1))^1;
	int p=0;
	if(a[1][0]>a[0][0])p=1;
	if(a[p].size() & 1)return 0;
	int cnt=0;
	while(a[p^1].size() && a[p^1][0]<a[p][0])
	a[p^1].pop_front(),++cnt;
	return work()^(cnt & 1);
}
int main()
{
	// freopen("in.txt","r",stdin);
	n=read();
	for(int i=1;i<=n;++i)
	w[i]=read();
	for(int i=1;i<=n;++i)
	{
		a[0].push_back(w[i]);
		if(w[i]>=w[i+1])break;
	}
	for(int i=n;i;--i)
	{
		a[1].push_back(w[i]);
		if(w[i]>=w[i-1])break;
	}
	if(!work())puts("Alice");
	else puts("Bob");
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值