Codeforces Round #721 (Div. 2) B2. Palindrome Game (hard version) (博弈)
题意
给定一个字符串,A和B轮流操作,A是先手;
每个人可以有如下的两种操作:
\(1:\)将字符串中某个位置的\(0\)换成\(1\),代价为\(1\)
\(2:\)将整个字符串翻转,代价为\(0\)
其中操作\(2\)的条件为:当前的字符串不是回文字符串并且上一次并没有执行过该操作
当字符串变为全\(1\)字符串时,游戏结束。代价小的获胜。
思路
先考虑\(easy\)版本,也就是开始都是回文字符串时,输赢跟\(0\)的个数有关:
- 如果当前有奇数个\(0\)时,由于是回文字符串,中心轴的位置一定为\(0\),假设为\(0000000\)
为了方便计算代价,假设先手改变的\(1\)用\(2\)来表示,后手改变的\(1\)用\(3\)来表示
先手会将中心轴位置的\(0\)变为\(1\),这样先手代价为\(1\),字符串变为\(0002000\)
由于当前字符串仍旧满足回文串,所以后手只能选择操作\(1\),后手代价为\(1\),字符串变为\(0032000\)
接下来的操作中,先手都在跟后手对称的位置进行操作\(1\),这样每次后手操作时,字符串都为回文串,所以只能执行操作\(1\)
当字符串变为\(3332220\)时,轮到先手操作,先手可以执行操作\(2\),这样字符串变为\(0222333\),后手只能再执行操作\(1\),字符串变为\(3222333\),最后先手的代价为\(3\),后手的代价为\(4\),先手赢。 - 如果当前有偶数个\(0\)时,假设为\(000000\)
先手只能执行操作\(1\),字符串变为\(002000\);后手也在对称的位置执行操作\(1\),这样类似于上一种情况,先手永远不会遇到回文串的情况,也就无法执行操作\(2\)
当字符串变为\(322330\)时,后手翻转字符串,得到\(033223\),先手执行操作\(1\),得到\(233222\),最后先手的代价为\(4\),后手的代价为\(2\),后手赢。 - 但是当只有1个0时,先手只能进行操作\(1\),后手赢。
\(hard\)版本如何考虑呢。
首先我们可以知道将原串变为回文串的代价,记作\(sum\)
- 如果\(sum>=2\)的话,先手必赢:假设字符串为\(111000\),由于字符串不是回文字符串,所以先手可以先执行操作\(2\),后手只能执行操作\(1\),直到游戏结束。
- 如果\(sum==0\)的话,就按照回文串的情况写
- 如果\(sum==1\)的话,考虑0的个数:如果只有2个0的话是平局,否则就是先手赢。
如果只有两个0说明中心轴一定为0,假设字符串为100,那么先手可以执行操作2,变为001,后手只能执行操作1,变为021,先手执行操作1,变为321,是平局;
否则的话,1101,先手执行操作2,后手执行操作1,先手代价小,先手赢。
代码
const int maxn =1e5+7;
char s[maxn];
void solve(){
int n=read;
cin>>s+1;
int cnt=0,sum=0;
for(int i=1;i<=n;i++)
if(s[i]=='0') cnt++;
for(int i=1;i<=n;i++)
if(s[i]=='1'&&s[n-i+1]=='0') sum++;
///cout<<sum<<" "<<cnt<<endl;
if(sum==0){
if(cnt==1){
cout<<"BOB"<<endl;
}
else if(cnt%2){
cout<<"ALICE"<<endl;
}
else puts("BOB");
}
else if(sum>=2){
puts("ALICE");
}
else if(sum==1){
if(cnt==2) puts("DRAW");
else puts("ALICE");
}
}
int main() {
int T=read;
while(T--){
solve();
}
return 0;
}