CodeForces 299C Weird Game

题目大意

直接拿样例讲:

input:
2
0111
0001
output:
First

第一行输入n,接下来两行,均为长度为2*n的字符串。
两个人ABA先手,从第一个字符串中选择n个数字,B从第二个字符串中选择n个数字,每轮选一个,轮流进行。
要求:

  1. 若第一个字符串的第i个数字被A选过了,那么第二个字符串的第i个数就不能被B选了,同理选了第二个字符串,第一个字符串对应位置也不能选了;
  2. 二者每次都进行最优选择;
  3. A胜输出FirstB胜输出Second,平局Draw

题解

我一直觉得这题一个式子应该就能出来结果,所以想算出式子直接输出,但是经过昨天一晚上的努力失败了,我太高估自己了,居然妄想一下写出式子来。于是今天先按思路实现了一下,直接一发过,之后又调整成了完美代码!!!调整后的代码格式直接爱了!!!这就是程序员的浪漫嘛♥


言归正传!
贪心思路:
step1: 二者都先选重合的,什么是重合的?就是同一个位置上,两个字符串都是1。两个人肯定要先争着抢到这些公共的,人都是贪心的,对于那些一定属于自己的1我们可以晚点再拿;
step2: 接下来,我们可以悠哉游哉地选那些一定属于自己的1了;
step3: 但是,可能存在这种情况。我们首先规定一定属于自己的1称为“npy”。如果存在一个人的 npy 个数很少,而另一个人的 npy 个数很多,当 npy 少的选完自己的 npy 后,看到另一个居然 npy 这么多,心生嫉妒,抱着“自己得不到别人也别想得到”的想法,就去破坏另一个人选 npy ,最终导致另一个人也没能将自己的 npy 全选上。

大致思路就分为上述三步,看着只有三步,但其实情况数还是很多的。


代码实现上我再稍微讲一下思路:
可以考虑到step1中重合的个数为奇数时,接下来的操作相当于B先手,而如果重合的个数为偶数,那么接下来还是相当于A先手。其实step1之后的某些情况中,A先手还是B先手没区别(需要自己揣摩了)。
step2中选自己的 npy 时也并非无所顾忌,要注意自己的 npy 可能很多,但是最多能选的很少。
step3本质上只是已经拥有比较多的 npy 的人之后得到少一点而已(不知道你能不能理解这里,不理解可以看代码)

代码(初稿,详细)

#include<bits/stdc++.h>
using namespace std;
int m, allA, allB, AB;
string s1, s2;
int main()
{
	cin>>m>>s1>>s2;
	int n = (m << 1);
	for(int i = 0;i < n;i ++) {
		if(s1[i] == '1') allA++; // A中1的总个数 
		if(s2[i] == '1') allB++; // B中1的总个数
		if(s1[i] == '1' && s2[i] == '1') AB++; // A和B重合的1的个数 
	} 
	
	int difA = allA - AB, difB = allB - AB; // 获取A和B不重复的1的个数 
	int restopA = n - (AB + 1)/2, restopB = n - AB/2; // A和B剩下可以进行选择的次数 
	// AB & 1 == 1 : A比B多操作一次
	
	
	// 建议先看else部分代码! 
	if(AB&1) { // 理解为B先手,这样与A先手其实是一样的,只不过最后注意 “A已经比B多一个1了” 
		// restopB = restopA + 1 : B比A多操作一次
		// 此时,A已经比B多一个1了 
		if(restopB <= difB && restopA <= difA) puts("Draw"); 
		else if(restopB > difB && restopA <= difA) {
			// 此轮操作中(重复的被选完了后的操作),最好的情况能保证B拿的与A拿的一样多,但是之前A多拿了1个,因此A必胜 
			puts("First");
		} else if(restopB <= difB && restopA > difA) {
			puts("Second");
		}
		else if(restopB > difB && restopA > difA) {
			// difA+1==difB||difA+2==difB 条件也是通过举例子判断的 
			difA>=difB?puts("First"):difA+1==difB||difA+2==difB?puts("Draw"):puts("Second");
		}
	} else { // restopA == restopB => restop
		// 在此之前二者拿到1的个数一致!!! 
		int restop = restopA;
		if(restop <= difB && restop <= difA) puts("Draw"); // 能进行拿1的次数太少了,所以每次都能拿自己的1,只与能操作的次数有关了 
		else if(restop > difB && restop <= difA) {
			// A剩下difA个1,B剩下0个1
			// A先选,B再选
			// A、B均剩下restop次操作机会 
			// !!!在此之前二者拿到的1个数一致,由于下一步A要拿,且B只能试图阻止A少拿,B的1个数不会增长,因此A必赢 
			puts("First");
		} else if(restop <= difB && restop > difA){
			// 与上面类似 
			// A剩下0个1,B剩下difB个1
			// A先选,B再选
			// A、B均剩下restop次操作机会
			// 同理在此之前二者拿到的1个数一致,由于下一步B要拿,且A只能试图阻止B少拿,A的1个数不会增长,因此B必赢 
			puts("Second");
		} else if(restop > difB && restop > difA){
			// 此之前二者拿到的1个数一致
			// 操作次数多,而1的个数少,npy不够就去破坏别人选npy 
			// 这里你可以举两个例子试一下,因为A先手,所以当A剩下的1个数与B剩下的1个数相等或者少一个时,都是平局
			// 当A剩下的1比B剩下的1多时,B选完自己1后会来破坏A选1,但只是让A选的1尽可能少,其实胜负已定,A win
			// 类似的,其他情况 B win 
			difA>difB?puts("First"):difA==difB||difA+1==difB?puts("Draw"):puts("Second");
		}
	}

	return 0;
}

代码(终稿,好看)

// ------ 太美了! ------ //
#include<bits/stdc++.h>
using namespace std;
char ans[][10] = {"Draw", "First", "Second"};
int n, m, allA, allB, AB;
string s1, s2;
int main()
{
	cin>>m>>s1>>s2;
	n = (m << 1);
	for(int i = 0;i < n;i ++) {
		if(s1[i] == '1') allA++;
		if(s2[i] == '1') allB++;
		if(s1[i] == '1' && s2[i] == '1') AB++;
	}
	
	puts(
		ans[
			n-AB/2 <= allB-AB && n-(AB+1)/2 <= allA-AB ? 0 :
			n-AB/2 >  allB-AB && n-(AB+1)/2 <= allA-AB ? 1 :
			n-AB/2 <= allB-AB && n-(AB+1)/2 >  allA-AB ? 2 :
			allA-AB>= allB-AB+!(AB&1) ? 1 :
			allA-AB+(AB&1) == allB - AB || allA-AB+(AB&1)+1 == allB - AB ? 0 : 2 
		]
	);
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不牌不改

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值