题目描述
难度分:2043
输入n(2≤n≤3×105)和长为n的严格递增数组a(0≤a[i]≤109)。
Alice
和Bob
在玩一个回合制游戏,Alice
先手。
游戏规则如下:
-
一开始,数轴上有n颗石子,第i颗石子的位置是a[i]。
-
每个回合,玩家只能移动最右边的那颗石子。且必须将它移动到在它左边的没有石子的非负整数空位上。例如 a=[2,4],你只能移动位置4上的石子到位置0或1或3。
-
移动石子后,轮到另一个玩家继续移动这n颗石子中的最右边的石子。如此交替。
-
无法移动的玩家输掉游戏,另一位玩家获胜。
如果Alice
必胜,输出Alice
,否则输出Bob
。
输入样例1
2
2 4
输出样例1
Alice
输入样例1
3
0 1 2
输出样例1
Bob
算法
打表找规律
完全不会做,先写了个DP
寻找必败态。
状态定义
dp[mask]表示在给出的数为mask状态时,Alice
能否获胜,mask上第i位为1就表示数字i是给出的。
状态转移
先找出mask最右边1的位置x,然后枚举将其放在前面各个为0的位置i。带来的效果就是第x位变成0,第i位变成1,因此状态转移方程为
dp[mask]=dp[mask−2x+2i]
只要有一个i位置能让Alice
回合的dp[mask]为true
,那么Alice
就是必胜的。
打印一下必败态发现规律:如果最大值和次大值相差1,并且除了它们两个数之外,剩余数的个数奇偶性与次大值是相同的,就是个必败态,Bob
获胜,否则就是Alice
获胜。
复杂度分析
时间复杂度
由于读入的数字本来就是有序的,因此判断一下最大值与次大值的差是否为1,然后判断n−2和次大值奇偶性是否相同就可以了,两个判断时间复杂度O(1)。
空间复杂度
仅适用有限几个变量,额外空间复杂度为O(1)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 6, M = 300010;
bool dp[(1<<N) + 10];
int n;
int a[M];
void get_loss() {
for(int mask = 0; mask < 1<<N; mask++) {
int x = -1;
for(int i = 0; i < N; i++) {
if(mask>>i&1) x = i;
}
bool loss = false;
for(int i = 0; i < x; i++) {
if(!(mask>>i&1)) {
loss |= !dp[mask - (1<<x) + (1<<i)];
if(loss) break;
}
}
dp[mask] = loss;
if(!dp[mask]) {
// 必败态
vector<int> ans;
for(int i = 0; i < N; i++) {
if(mask>>i&1) {
ans.push_back(i);
printf("%d ", i);
}
}
puts("");
}
}
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
if(a[n] - a[n - 1] == 1 && (((n - 2)&1)^(a[n - 1]&1)) == 0) puts("Bob");
else puts("Alice");
return 0;
}