uva1610 聚會遊戲(細節處理)
阿新 • • 發佈:2018-02-18
cout pac const ets 部分 max names pre pos
uva1610 聚會遊戲(細節處理)
輸入一個n(n<=1000且為偶數)個字符串的集合D,找一個長度最短的字符串(不一定要在D中出現)S,使得D中恰好一半字符串小於等於S,另一半大於S。如果有多解,輸出字典序最小的解。
首先找到兩個中位字符串S1和S2。顯然,答案S必須夾在S1和S2之間。由於要找最短的字符串,我們可以用叠代加深的思路,根據|S|叠代加深枚舉。每次枚舉長度時,必須保證S的前|S|-1位和S1相同,然後最後一位枚舉26個字符。這是一個貪心,應該不難證。我用了第k大數和跳過公共前綴部分來加速。
#include <cstdio>
#include <string>
#include <iostream>
using namespace std;
const int maxn=1005;
int n;
string a[maxn], s1, s2;
void getstr(int l, int r, int ord, string &str){
if (l>=r) return;
string base=a[(l+r)>>1]; int now=l;
for (int i=l; i<r; ++i) //比base小的區間是[l,tmp)
if (a[i]<base) swap(a[i], a[now++]);
int tmp=now;
for (int i=tmp; i<r; ++i) //等於base的區間是[tmp,now)
if (a[i]==base) swap(a[i], a[now++]);
if (ord<tmp) getstr(l, tmp, ord, str);
else if (ord>=now) getstr(now, r, ord, str);
else str=a[tmp];
}
string getpre(string s1, string s2){
string s("");
if (s1.size()>s2.size()) swap(s1, s2);
for (int i=0; i<s1.size(); ++i)
if (s1[i]==s2[i]) s+=s1[i]; else break;
return s;
}
int main(){
//註意半開區間取中值的方法和閉區間不同!
while (~scanf("%d", &n)&&n){
for (int i=0; i<n; ++i) cin>>a[i];
//獲取兩個中位字符串 復雜度O(n|s|) s=30
getstr(0, n, n/2-1, s1); getstr(0, n, n/2, s2);
string s=getpre(s1, s2); int cur=s.size();
if (s>=s1&&s<s2){ cout<<s<<endl; continue; }
while (true){
for (char c=s[cur]; c<='Z'; ++c)
if (s+c>=s1&&s+c<s2){
cout<<s+c<<endl; goto end; }
s+=s1[cur++];
}
end:;
}
return 0;
}
uva1610 聚會遊戲(細節處理)