【思維題】【貪心】【模擬】【CodeForces】794 C Naming Company
【題意】
兩個人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; }