vijos 拓撲編號
阿新 • • 發佈:2017-07-06
-- 輸出 逆拓撲排序 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 拓撲編號