1. 程式人生 > >vijos 拓撲編號

vijos 拓撲編號

-- 輸出 逆拓撲排序 size std col 描述 nbsp emp

描述

H國有n個城市,城市與城市之間有m條單向道路,滿足任何城市不能通過某條路徑回到自己。

現在國王想給城市重新編號,令第i個城市的新的編號為a[i],滿足所有城市的新的編號都互不相同,並且編號為[1,n]之間的整數。國王認為一個編號方案是優美的當且僅當對於任意的兩個城市i,j,如果i能夠到達j,那麽a[i]應當<a[j]。

優美的編號方案有很多種,國王希望使1號城市的編號盡可能小,在此前提下,使得2號城市的編號盡可能小...依此類推。

格式

輸入格式

第一行讀入n,m,表示n個城市,m條有向路徑。

接下來讀入m行,每行兩個整數:x,y
表示第x個城市到第y個城市有一條有向路徑。

輸出格式

輸出一行:n個整數
第i個整數表示第i個城市的新編號a[i],輸出應保證是一個關於1到n的排列。

樣例1

樣例輸入1

5 4
4 1
1 3
5 3
2 5
Copy

樣例輸出1

2 3 5 1 4
Copy

限制

每個測試點1s

提示

30%的測試點滿足:n <= 10, m <= 10
70%的測試點滿足:n <= 1000, m <= 10000
100%的測試點滿足:n <= 100000, m <= 200000
輸入數據可能有重邊,可能不連通,但保證是有向無環圖。

來源

Topcoder

/*逆拓撲+貪心,從後往前,先找編號最大的

證明:
    假設我們按逆拓撲排序的方法求出了一個拓撲序列(把得到的反序列正過來),記為A。
    假設最優解的拓撲序列是B。
    從後往前比較AB,設在位置k,AB第一次出現不同。即A[k]!=B[k],A[p]=B[p].
    顯然根據我們的貪心策略"每次取的是編號最大的",有A[k]>B[k].
    那麽我們在B中取尋找A[k],即找到B[p]=A[k].
    然後把B中B[p],B[p+1]...B[k]這一段拿出來,記為序列C。
    因為B[p]=A[k] , 把B[p]換成A[k],C=A[k],B[p+1]....B[k].
    很顯然在C中A[k]不是最小的,因為至少B[k]比它小。 假設C中最小的是B[q],那麽我們可以構造出一個序列D.
    D=B[p+1],B[p+2]...B[q],A[k],B[q+1]...B[k]. (實質就是把A[k]移到B[q]的後面)
    顯然這個序列會比序列C更優,因為B[q]的名次靠前了一名。 那麽如果把C換成D會更優,與B是最優解矛盾。
    那麽怎麽知道序列D一定是合法的呢?因為如果A[k]恰好是B[p+1]的前驅,那麽就不能把A[k]移走。
    所以我們回到序列A,序列A中A[k]是AB序列從右往左第一個不同的元素,那麽在A中,B[p+1]肯定是在A[k]前面的,所以A[k]不可能是B[p+1]的前驅。
    綜上,我們得到的答案就是最優解。

如果不理解,按照樣例舉個例子
         4 -> 1 -> 3 <-5 <-2;
        如果貪心,每次找入度為0的點,若有多點,找編號小的點:
            第一次找到 2;
            第二次找到 4;
            第三次找到 1;
            第四次找到 5;
            第五次找到 3;
        這顯然是錯誤的:

        按照上面方法 逆拓撲,從後往前,先找編號最大的
        1ci find 3, 3 number 5;
        2ci find 5, 5 number 4;
        3ci find 2, 2 number 3;
        4ci fine 1, 1 number 2;
        5ci fine 4, 4 number 1;
        fit answer;
*/ #include<iostream> #include<cstdio> #include<vector> #include<queue> using namespace std; const int maxn = 100000 + 10; const int maxm = 200000 + 10; int n, m; int cloc; int cnt[maxn], id[maxn]; vector<int> G[maxn]; priority_queue<int> Q; int main () { scanf("%d%d"
,&n,&m); while (m--) { int u, v; scanf("%d%d", &u, &v); u--; v--; cnt[u]++; G[v].push_back(u); } for (int i = 0; i < n; i++) if (!cnt[i]) Q.push(i); cloc = n; while (!Q.empty()) { int u = Q.top(); Q.pop(); id[u] = cloc--; for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; cnt[v]--; if (cnt[v] == 0) Q.push(v); } } for (int i = 0; i < n; i++) printf("%d ", id[i]); return 0; }

vijos 拓撲編號