1. 程式人生 > >反向拓撲排序 HDU 4857 逃生

反向拓撲排序 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;
}