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;
}

参考

posted @ 2021-05-21 11:02  OvO1  阅读(91)  评论(2编辑  收藏  举报