時間限制:2.000秒
題目鏈接::http://codeforces.com/problemset/problem/510/C
這是昨天,啊不對,是今天凌晨CF的題,其實就是個簡單的拓撲排序的題,結果知識生疏,一開始愣是沒看出來是個拓撲排序,後來反應過來又心急各種寫不對。最後就出了倆水題,不過因爲之前確實太渣這次居然還是漲分了……多少過了1500變藍了,不知道還能不能保持住……
今天又做了一遍,算是複習拓撲排序吧。
題目就是給了一堆完全由小寫字母構成的字符串,這些字符串可能是按照某種字典序排列的,讓你求是否存在這種字典序,如果存在,讓你求出來其中一種可能的字典序。
前面說了這是個拓撲排序的題,那啥是拓撲排序呢。
借用百度百科的說法,就是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊<u,v>∈E(G),則u在線性序列中出現在v之前。通常,這樣的線性序列稱爲滿足拓撲次序(Topological Order)的序列,簡稱拓撲序列。這樣看來,這道題就是一道拓撲排序的水題了。
然後就是拓撲排序怎麼排的問題了。拓撲排序怎麼排呢,其實也很簡單,每次找一個入度爲0的點,即不作爲任何邊的終點的點,輸出該點,然後刪除該點和所有從該點出發的邊,然後重複上述步驟,直到G爲空爲止。
但是這道題還有個問題就是有可能無解,無解代表着建立的圖有環,就類似a>b, b>c, c>a沒辦法判斷大小關係一樣,此時就無法進行拓撲排序。那麼怎麼檢測圖有沒有環呢?這個也好辦,在進行上面的拓撲排序的步驟的時候,如果G不爲空,且剩下的點入度都不爲0,那麼就代表圖有環,無解。
另外無解的時候還有一種情況就是不滿足字典序對長度的定義,例如:
abcde
abc
顯然“abc”應該在“abcd”的前面。根據上面的思路就能寫出代碼A掉了。這也是個教訓吧……知識掌握不牢靠……
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int n;
string s[110];
int in[300];
bool exist[300][300];
vector<char> G[300];
string answer;
bool GetEdge(const string &a, const string &b) { // 根據字符串的關係建立有向邊
for(int i = 0; i != a.size() && i != b.size(); ++i) if(a[i] != b[i]) {
if(!exist[a[i]][b[i]]) {
++in[b[i]];
G[a[i]].push_back(b[i]);
exist[a[i]][b[i]] = true;
}
return true;
}
return false;
}
bool GetGraph() {
for(int i = 0, j = 1; j != n; ++i, ++j) {
if(!GetEdge(s[i], s[j]) && s[i].size() > s[j].size()) return false; // 不滿足字典序的排列方法
}
return true;
}
bool Less(const char &a, const char &b) {
return in[a] > in[b];
}
bool PutAns() {
string cal = "abcdefghijklmnopqrstuvwxyz";
while(!cal.empty()) { // 拓撲排序
sort(cal.begin(), cal.end(), Less);
char &c = cal[cal.size() - 1];
if(in[c] != 0) return false; // 入度最小的點不爲0,無解
for(int i = 0; i != G[c].size(); ++i) --in[G[c][i]];
answer += c;
cal.resize(cal.size() - 1);
}
return true;
}
int main() {
ios::sync_with_stdio(false);
cin >> n;
for(int i = 0; i != n; ++i) cin >> s[i];
if((!GetGraph()) || (!PutAns())) cout << "Impossible" << endl;
else cout << answer << endl;
return 0;
}