[Gym101653Q]Number Game
[Gym101653Q]Number Game
题目大意:
\(T\) 组数据。给定一个 \(1\sim n\) 的全排列,Alice 和 Bob 轮流取数。一个数能被取走,当且仅当这个数紧邻两侧没有比它大的数。取走 \(1\) 的人获胜。两人都按最优策略进行游戏。
\(T\le 100; n\le 100\)
思路:
当 \(1\) 紧邻两侧仅剩一个数时,Alice 和 Bob 肯定都不愿主动取走这个数(“山峰”),因为这样一来,就能让对方把 \(1\) 取走,然后自己就输了。
有题意得 Alice 和 Bob 都聪明绝顶,为了避免陷入这样的绝境,他们肯定会想方设法先取走别的数。直到不得不面对这一绝境时,输家取走“山峰”,而赢家则将 \(1\) 取走。
此时,剩下的数只有受“山峰”支配的数,在原数列中具体体现为从山峰出发,往 \(1\) 反方向延伸的一段“下坡”。设“坡长”为游戏结束后剩余数的个数,亦即排除山峰以后“下坡”的长度,设其为 \(\ell\),那么游戏进行的总步数为 \(n - \ell\)。显然,若 \((n - \ell) \mod 2 = 1\) 则 Alice 胜,否则 Bob 胜。
当 \(1\) 在两端时,“下坡”是唯一的(“坡长”可以为 \(0\));但是当它在中间时,则会在两侧各形成一段“下坡”。不妨设 \(1\) 的位置为 \(p\),左边的“坡长”为 \(\ell_l\),右边的为 \(\ell_r\),我们可进行如下分类讨论(为使表达简练,此处匹配到第一个条件则停止匹配后续条件):
- \(p = 1\):最后留下“右坡”共 \(\ell_r\) 个数,游戏总步数为 \(n - \ell_r\);
- \(p = n\):最后留下“左坡”共 \(\ell_l\) 个数,游戏总步数为 \(n - \ell_l\);
- \(\ell_l = 0 \vee \ell_r = 0\):由于 \(1\) 受至少一侧的“上坡”所支配,若两人都按最优策略进行游戏,\(1\) 一定会在“上坡”取完后,成为整个游戏最后被取走的数,故总步数为 \(n\);
- \(\ell_l\mod 2\ne\ell_r\mod 2\):作为先手,Alice 可以任选一边开始游戏,使另一边剩下,总会有一种办法使得总步数为奇数,故赢家一定是 Alice;
- \(\ell_l\mod 2 = \ell_r\mod 2\):无论 Alice 从哪边开始游戏,总步数的奇偶性都相同,结局都是一样的,此时只能随便选一边,然后听天由命。
最后根据游戏总步数的奇偶性判断胜负即可。
时间复杂度 \(\mathcal O(n)\)。
源代码:
#include<cstdio>
#include<cctype>
#include<algorithm>
inline int getint() {
char ch;
while(!isdigit(ch=getchar()));
int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
constexpr int N=101;
int a[N];
int main() {
for(int T=getint();T;T--) {
const int n=getint();
for(int i=1;i<=n;i++) a[i]=getint();
const int p=std::find(&a[1],&a[n]+1,1)-a;
int len1=0,len2=0;
for(int i=p-2;i>=1&&a[i]<a[i+1];i--) len1++;
for(int i=p+2;i<=n&&a[i-1]>a[i];i++) len2++;
int take=0;
if(p==1) {
take=n-len2;
} else if(p==n) {
take=n-len1;
} else if(len1==0||len2==0) {
take=n;
} else if(len1%2!=len2%2) {
take=n-((n-len1)%2?len1:len2);
} else {
take=n-len1;
}
puts(take%2?"Alice":"Bob");
}
}