1. 程式人生 > >HDU 4857 逃生 【拓撲排序+反向建圖+優先隊列】

HDU 4857 逃生 【拓撲排序+反向建圖+優先隊列】

all 人在 CM sign iss define debug pro sstream

逃生
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6686 Accepted Submission(s): 1958

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

Author

CLJ
【題意】:有N個人,M個優先級a,b表示a優先於b,並且每個人有個編號的優先級,輸出順序。
---

拓撲排序,因為需要盡可能讓編號較小的在前面,比如有如下約束

1->4->2 5->3->2

如果我們考慮正向建圖的話得出的拓撲序列為 1 4 5 3 2

但是實際上,在圖中 1 4 與 5 3 之間並沒有約束,並且負責人在排序的時候會先去考慮 3 號的位置,於是有另一種結果 1 5 3 4 2 。

可見,第二種結果才是正確的,因為較小的編號總是優先選擇靠前。

因為正向建圖求拓撲序列的過程中,編號較小的點可能會被更大的點擋住,於是我們采用逆向建圖的方法,然後用大頂堆維護,把編號較大的點先選出來,最後反轉數組即可。

 題意大抽象後概是這樣的:給定一個有向圖,將其按線性優先級輸出。題目大致一掃,可以明白是個赤果果的拓撲排序。以前學過一點拓撲排序,後來給忘掉

了。拓撲排序就是解決對有向圖的點進行線性排序的相關問題。假設給定一標記優先級的有向圖,那麽每個有向邊通向的點的入度都要+1,這樣的標記後,入度為

0的點就可以看成是優先級最高的點(因為沒有點在它前面去連接它)。把此點取出,與這個點相連的其他點的邊也就全部都要消掉了,同時將它們的入度減去1;

這樣會產生新的入度為0的點,那麽這個點就是剩下的點中的優先級最高的點,重復上述的更新過程即可。此題有坑,可能會有多個拓撲環;還有此題要求輸出的點

如果沒有拓撲關系則按照小編號在前的方式排列。看到這個條件第一反應是在取優先級高的點的過程中用優先隊列去寫,先出隊列的就是編號較小的,這麽想似乎沒

錯。但是後來debug了下,假如有一有向圖關系是這樣的:6->3->1;5->4->2;這樣的話按照最小堆來進行拓撲排序會出這樣的結果:5 4 2 6 3 1;但是實際上

我們可以把6 3 1 這個拓撲環放在前面,生成6 3 1 5 4 2這樣的線性序列。究其原因,我們優先隊列先彈出的是編號較小的符合條件的點,但實際上這個點只能算

是該拓撲環排序後的頭結點,我們想要保證讓小號的全部盡可能地放在前面,我們需要保證拓撲環的尾部的小號也在前面。這樣來想一下可以發現先出編號小的是

不完善的。於是我們可以考慮逆向思維,先彈出序號大的,那麽這樣先出隊列的就是編號最大的;但是我們有需要編號最大的在最後輸出,換句話說就是編號最大的

是作為優先級最低的;那麽我們把有向圖也給逆向儲存,這樣按優先級條件彈出的順序才會一致(優先彈出優先級最低的)。大致思路明白後代碼實現就很簡單了。

#include<cstdio>
#include<string>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<cctype>
#include<stack>
#include<sstream>
#include<list>
#include<assert.h>
#include<bitset>
#include<numeric>
#define debug() puts("++++")
#define gcd(a,b) __gcd(a,b)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define fi first
#define se second
#define pb push_back
#define sqr(x) ((x)*(x))
#define ms(a,b) memset(a,b,sizeof(a))
#define sz size()
#define be begin()
#define pu push_up
#define pd push_down
#define cl clear()
#define lowbit(x) -x&x
#define all 1,n,1
#define rep(i,n,x) for(int i=(x); i<(n); i++)
#define in freopen("in.in","r",stdin)
#define out freopen("out.out","w",stdout)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> P;
const int INF = 0x3f3f3f3f;
const LL LNF = 1e18;
const int maxn = 1e5 + 20;
const int maxm = 1e6 + 10;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int dx[] = {-1,1,0,0,1,1,-1,-1};
const int dy[] = {0,0,1,-1,1,-1,1,-1};
const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

int n,m,num,ok,u,v;
/*
4—>2
5—>1—>3
正常情況下字典序最小的拓撲序應該是:4 2 5 1 3
這種規則下,則應該是: 5 1 4 2 3
*/
vector<int> G[maxn];
int ans[maxn];

char s[10];
int inDeg[maxn],x;

void topSort()
{
    priority_queue<int> q; //優先隊列默認的是數據大的優先級高
    int num=0;
    while(!q.empty()) q.pop();
    for(int i=1;i<=n;i++) if(!inDeg[i]) q.push(i);
    while(!q.empty())
    {
        int now = q.top();
        q.pop();
        ans[++num]=now;
        for(int i=0; i<G[now].size(); i++)
        {
            int nxt = G[now][i];
            if(--inDeg[nxt] == 0) q.push(nxt);
        }
    }

}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        ms(inDeg,0);
        ms(ans,0);
        for(int i=1;i<=n;i++) G[i].clear();
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            G[v].push_back(u);
            inDeg[u]++;
        }
        topSort();
        for(int i=n;i>=1;i--)
        {
            if(i!=1) printf("%d ",ans[i]);
            else printf("%d\n",ans[i]);
        }
    }
    return 0;
}
/*
1
5 10
3 5
1 4
2 5
1 2
3 4
1 4
2 3
1 5
3 5
1 2
*/

HDU 4857 逃生 【拓撲排序+反向建圖+優先隊列】