题目大意
直接拿样例讲:
input:
2
0111
0001
output:
First
第一行输入n
,接下来两行,均为长度为2*n
的字符串。
两个人A
和B
,A
先手,从第一个字符串中选择n
个数字,B
从第二个字符串中选择n
个数字,每轮选一个,轮流进行。
要求:
- 若第一个字符串的第
i
个数字被A
选过了,那么第二个字符串的第i
个数就不能被B
选了,同理选了第二个字符串,第一个字符串对应位置也不能选了; - 二者每次都进行最优选择;
A
胜输出First
,B
胜输出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;
}