HDu4857逃生 反向拓撲排序來出字典序最小解
阿新 • • 發佈:2018-12-15
糟糕的事情發生啦,現在大家都忙著逃命。但是逃命的通道很窄,大家只能排成一行。
現在有n個人,從1標號到n。同時有一些奇怪的約束條件,每個都形如:a必須在b之前。
同時,社會是不平等的,這些人有的窮有的富。1號最富,2號第二富,以此類推。有錢人就賄賂負責人,所以他們有一些好處。
負責人現在可以安排大家排隊的順序,由於收了好處,所以他要讓1號儘量靠前,如果此時還有多種情況,就再讓2號儘量靠前,如果還有多種情況,就讓3號儘量靠前,以此類推。
那麼你就要安排大家的順序。我們保證一定有解。
Input
第一行一個整數T(1 <= T <= 5),表示測試資料的個數。
然後對於每個測試資料,第一行有兩個整數n(1 <= n <= 30000)和m(1 <= m <= 100000),分別表示人數和約束的個數。
然後m行,每行兩個整數a和b,表示有一個約束a號必須在b號之前。a和b必然不同。
Output 對每個測試資料,輸出一行排隊的順序,用空格隔開。 **分析** 首先先處理特殊關係,對於有要求的數字,在他們倆之間建立一條邊,a->b 同時b的入度++,表示b的限制條件增加1.那麼在處理時,用一個優先佇列按照greater的順序push,(push入度為0)那麼這道題就得到解決了,然後在這道題中是行不通的,因為對於這樣一組資料3 1一共有5個點的情況下,那麼按照上面的方法那麼就會輸出2 3 1,然而這並不符合題意因為題目中要求是1要儘可能上前,因該輸出的是3 1 2,那麼該如何處理這個問題喃?首先應該分析為什麼會出現這個問題,是因為在優先佇列《greater》下因為2的入度為0,所以會先入隊,那麼應該怎麼讓3先入隊?大家都是入隊為0,並且老子還抱著個大腿啊喂!於是就考慮到反向拓撲,然後當然也要反向建圖是不是。優先佇列也要按照less的順序排序,這樣做的結果是5 4 2 1 3然後再反向輸出就GG了,不要忘記重邊的判斷哦。 程式碼:
#include<cstdio> #include<cstdlib> #include<iostream> #include<set> #include<stack> #include<cstring> #include<vector> #include<queue> using namespace std; const int maxn = 3e4+10; int in[maxn]; int n, m; stack<int>iron; vector<int>vec[maxn]; typedef pair<int, int>pii; void ini() { memset(in, 0, sizeof(in)); for (int i = 0; i <= n; i++) vec[i].clear(); while (!iron.empty()) iron.pop(); } void solve() { priority_queue <int, vector<int> >que; for (int i = 1; i <= n; i++) { if (in[i] == 0) que.push(i); } while (!que.empty()) { int t = que.top(); iron.push(t); que.pop(); for (int i = 0; i < vec[t].size(); i++) { int p = vec[t][i]; if (--in[p] == 0) que.push(p); } } } void show() { while (iron.size() > 1) { printf("%d ", iron.top()); iron.pop(); } printf("%d\n", iron.top()); } int main() { int t; scanf("%d", &t); while (t--) { scanf("%d %d", &n, &m); ini(); set<pii>s; for (int i = 0; i < m; i++) { int a, b; scanf("%d %d", &a, &b); pii p = make_pair(b, a); if (s.count(p) == 0) { s.insert(p); vec[b].push_back(a); in[a]++; } } solve(); show(); } // system("pause"); return 0; }