【题目】http://codeforces.com/problemset/problem/794/C
【题意】
两个人A和B在玩一个填字母游戏。现在A和B都有一个包含 n 个小写字母的多重集合(可以有重复字符)。
初始有一个长度为n的空字符串s,两人轮流操作,A先手。一次操作可以将自己集合中的一个字母拿出来,放到字符串s的某个空位置,然后把这个字母从自己集合中删除(如果有多个只删一个)。直到字符串s被填满游戏结束。
A的目标是让字符串s字典序最小,而B的目标是让字符串s字典序最大。假设两人都无比聪明,问游戏结束后字符串s是怎么样的。
Input
第一行包含一个字符串 s ,长度为 n (1 ≤ n ≤ 3·105). 每个字符都是小写英文字母,表示A拥有的多重集合。
第二行包含一个字符串 t 长度为 n。每个字符都是小写英文字母,表示B拥有的多重集合。
Output
输出为一个长度为 n 的小写英文字母串,表示所有操作完之后的字符串。
【思路】
我们可以知道,A只有两种策略:1:将自己最小的字符放到答案的最前面;2:或者将自己最大的字符放在最后面。
当A中的字符全部>=B中的字符,i.e.自己最小的字符>=B最大的字符时,就应该把自己最大的字符尽可能往后放,也就是选择第二种方式。这样就会逼迫B把字典序小的字符放在前面。
当A里有比B中小的字符,即自己最小的字符<B最大的字符时,就应该让这个最小的字符占据尽可能靠前的位置,也就是选择第一种方式。
同理,B也只有两种策略:1:将最大的字符放在最前面;2:将最小的字符放在最后。
当B中的字符全部<=A中的字符,即B的最大的字符<=A最小的字符时,就应该把最小的字符尽可能往后放,让A不得不把字典序大的字符放在前面。
当B中存在比A中任意字符大,即B的最大字符>A最小的字符时,就应该尽可能让这个字符靠前。
另外注意:当是奇数的时候,A会多操作一次,所以A的备选串长度是n/2+1;否则AB都是n/2。
当是奇数时,A多操作的那一次B的备选串已经为空,无需比较直接输出即可。
【代码】
#include<bits/stdc++.h>
#define fuck(x) std::cout<<"["<<#x<<"->"<<x<<"]"<<endl;
using namespace std;
typedef long long ll;
const int M=3e5+5;
const int inf=1e9+5;
char x[M];
deque<char>q1;//双向队列储存已经排好序的备选串
deque<char>q2;
stack<char>s;//用栈储存尾巴
bool cmp(char a,char b)
{
return a>b;
}
int main()
{
scanf("%s",x);
int len=strlen(x);
sort(x,x+len);//A取小字符
int t;
if(len%2==1)
t=len/2+1;
else
t=len/2;
for(int i=0; i<t; i++)//len为奇数时,A会1个字符
{
q1.push_back(x[i]);
}
scanf("%s",x);
sort(x,x+len,cmp);//B取大字符
for(int i=0; i<len/2; i++)
{
q2.push_back(x[i]);
}
for(int i=0; i<len; i++)
{
if(i%2==0)//当A操作时
{
if(q2.empty())//如果是len奇数的最后一次操作
{
cout<<q1.front();//直接输出
q1.pop_front();
}
else if(q1.front()>=q2.front())
//如果A中字符全部>=B中字符
{
s.push(q1.back());//把最大的字符放到后面
q1.pop_back();
}
else
//如果存在字符<B中任意字符
{
cout<<q1.front();//把最小的字符放到前面
q1.pop_front();
}
}
else//当B操作时
{
if(q2.front()<=q1.front())
//如果B中字符全都<=A中字符
{
s.push(q2.back());//把最小的字符放后面
q2.pop_back();
}
else//如果存在字符>A中任意字符
{
cout<<q2.front();//把最大的字符放前面
q2.pop_front();
}
}
}
while(!s.empty())//输出尾巴
{
cout<<s.top();
s.pop();
}
return 0;
}