反向拓撲排序 HDU 4857 逃生
Problem Description
糟糕的事情發生啦,現在大家都忙著逃命。但是逃命的通道很窄,大家只能排成一行。
現在有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
對每個測試資料,輸出一行排隊的順序,用空格隔開。
Sample Input
1 5 10 3 5 1 4 2 5 1 2 3 4 1 4 2 3 1 5 3 5 1 2
Sample Output
1 2 3 4 5
為什麼要反向拓撲排序呢?舉個例子:6 -> 3 -> 1 和 5 -> 4 -> 2 。直接拓撲排序的結果是:5 4 2 6 3 1 ,結果是錯誤的,因為我們可以把1號安排到更前面的位置 即:6 3 1 5 4 2(正確答案)。
所以我們用逆向思維思考一下先考慮哪些點應該靠後釋放,這樣的話我們就可以把拓撲關係反過來(即弧頭和弧尾調換),然後做拓撲排序,然後我們可以根據原來的弧頭(現在變成弧尾)的大小來釋放結點,由於現在的問題變成哪些最後釋放,那麼就應該優先考慮弧尾(原來的弧頭)比較大的,可以通過優先佇列來解決。
#include<bits/stdc++.h> #define ll long long #define INF 0x3f3f3f3f #define maxn 110 #define qx std::ios::sync_with_stdio(false) #define N 2100 using namespace std; int in[31000],a,b,m,n; vector<int> v[30100]; int main(){ int t;qx;//cin太慢會超時 cin>>t; while(t--){ cin>>n>>m; for(int i=1;i<=n;i++) v[i].clear(); memset(in,0,sizeof in); vector<int> ans; priority_queue<int>q; for(int i=0;i<m;i++){ cin>>a>>b; v[b].push_back(a); in[a]++; } for(int i=1;i<=n;i++){ if(in[i]==0){ q.push(i); } } while(!q.empty()){ int p=q.top();q.pop(); ans.push_back(p); for(int i=0;i<v[p].size();i++){ in[v[p][i]]--; if(in[v[p][i]]==0) q.push(v[p][i]); } } for(int i=ans.size()-1;i>0;i--) cout<<ans[i]<<" "; cout<<ans[0]<<endl; } return 0; }