一道不太正经的博弈论题,可用递归解决
题意
给出一个序列,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;
}