题目
思路
很容易写出一个
O
(
n
2
)
\mathcal O(n^2)
O(n2) 的
d
p
\tt dp
dp 。然后把表输出。然后发现了美妙的规律。注明:下面所说的并不包含一开始游戏就结束了的情况。那样还玩个屁。
当长度是偶数(或者长度为奇数,但是中间的位置已经是 1 1 1 了)时:
- 如果有 i ( i > 0 ) i\;(i>0) i(i>0) 个位置不对称,那么 A l i c e \tt Alice Alice 会比 B o b \tt Bob Bob 少用 i i i 元。
- 如果原本就是回文串,那么 B o b \tt Bob Bob 会比 A l i c e \tt Alice Alice 少用 2 2 2 元。
当长度是奇数(并且中间的位置为零)时:
- 如果对称的位置存在 00 00 00 组合,记 i i i 为不对称位置的个数,那么 A l i c e \tt Alice Alice 比 B o b \tt Bob Bob 少用 i + 1 i+1 i+1 元。
- 如果对称的位置不存在 00 00 00 组合,记 i i i 为不对称位置的个数,那么 A l i c e \tt Alice Alice 比 B o b \tt Bob Bob 少用 i − 1 i-1 i−1 元(是的, i = 0 i=0 i=0 可以让 B o b \tt Bob Bob 获胜,这种情况其实就是单个 0 0 0 啊……)。
解释起来,感性理解一下,也很简单。由于总的 “零变一” 操作次数是固定的,所以只需要尽可能翻转。而不公平的操作机制——对手翻转后,不能立即翻转——导致 先手可以一直只翻转。
什么时候为止呢?只有一个位置不对称。这时候,如果仍然翻转, B o b \tt Bob Bob 操作后得到对称串,我无法翻转,主动权就落入了 B o b \tt Bob Bob 手中。不好!干脆就开始对称操作,保持串的对称性,让 B o b \tt Bob Bob 永远不能执行翻转操作。
然而这是不是最优呢?不是。剩下最后一个 00 00 00 型对称时, B o b \tt Bob Bob 操作一次,我们可以翻转。然后 B o b \tt Bob Bob 又操作完,得到 11 11 11,对称了,游戏也结束了。他赚不到便宜。——这也说明了,为什么我们要对称操作:一旦 B o b \tt Bob Bob 掌握主动权,他至少可以用这一招牟取 2 2 2 元的利润,而当时我们仍然翻转,无非让 B o b \tt Bob Bob 多填了一个 0 0 0 。
如果有 x x x 个位置不对称,那么 x − 1 x-1 x−1 个位置都是 B o b \tt Bob Bob 操作为对称的,最后 1 1 1 个是 A l i c e \tt Alice Alice 填好的,剩下的多数都是一人填一下,除了最后 B o b \tt Bob Bob 又多操作两次。所以 A l i c e \tt Alice Alice 赚到 x x x 。
如果串原本没有 00 00 00 型对称呢?那么,只要把所有不对称的位置搞定,游戏就结束了。所以 A l i c e \tt Alice Alice 会一直翻转,永远不花一分钱。这种情况 A l i c e \tt Alice Alice 也赚到 x x x 元,恰好与上面的可以合并在一起。
显然 x = 0 x=0 x=0 时上面的结论不太对。因为此时 A l i c e \tt Alice Alice 一来就没办法翻转,而 B o b \tt Bob Bob 就会用这招倒打一耙。然后 B o b \tt Bob Bob 赚到两元,赢了。
如果正中间还有一个零呢?那么 B o b \tt Bob Bob 即使操作为对称串,我也还有这一步可以操作。当然,也可能 B o b \tt Bob Bob 提前填好。无论哪种情况,反正相当于 A l i c e \tt Alice Alice 多赚到一个。即赚到 x − 1 x-1 x−1 元。
可是我们要注意一下,没有 00 00 00 型对称的情况。此时 A l i c e \tt Alice Alice 不能在最后倒打一耙。因为此时 B o b \tt Bob Bob 不会主动把中间填上,最终 A l i c e \tt Alice Alice 会把它填好,然后游戏就结束了。只能赚 x − 1 x-1 x−1 元了。
把上面所说的综合起来,就是开头的结论了。一个问题是,我似乎没有说明这是最优的?其实你可以自己揣摩一下。如果 A l i c e \tt Alice Alice 不抢占翻转能力,那么 B o b \tt Bob Bob 相当于新局面的先手,他可以赚不少(就用上面的策略),所以 A l i c e \tt Alice Alice 不得不这么做。
当然,如果你只想判断谁能赢,就只需要找到这种必胜策略就够了。时间复杂度 O ( n ) \mathcal O(n) O(n) 。
代码
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long int_;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MaxN = 100005;
char str[MaxN];
int main(){
for(int T=readint(); T; --T){
int n = readint();
scanf("%s",str);
int x = 0; bool f = false;
for(int i=0; i<n-1-i; ++i)
if(str[i] != str[n-1-i])
++ x; // not Palindrome
else if(str[i] == '0')
f = true; // both zero
if(!(n&1) || str[n>>1] == '1')
puts(x ? "ALICE" : "BOB");
else if(f || x >= 2)
puts("ALICE"); // n is odd
else puts(x ? "DRAW" : "BOB");
}
return 0;
}