【拓撲排序】BZOJ4010-[HNOI2015]菜餚製做

【題目大意】html

是要求N個點的一個拓撲序,且知足如下條件:編號1的位置儘量靠前,在知足全部限制,編號2的位置儘量靠前,以此類推。c++

【思路】算法

一開始以爲優先隊列維護一下拓撲就行了。然而樣例告訴咱們是不能夠的。若是限制條件是:
5 2
4 3post

最後出來的會是1-4-3-5-2,而答案應該是1-5-2-4-3。spa

由此能夠發現,若是正向拓撲出來的是「字典序最小」,而不是「編號小的儘量靠前」。code

因此逆向拓撲。htm

證實……算了困了,改天再糾結吧。丟個連接存個檔。blog

不妨認爲咱們這樣獲得的不是最優解,那麼令這樣獲得的序列爲a,而後最優解是b。
咱們從後往前開始找到第一位兩個序列不一樣的一位設爲k,那麼a[k]!=b[k],且a[k]>b[k]。(由a的構造方式可知)(先假設這個k存在,再證出矛盾)
再設a[k]出現的b的p位置,即b[p]=a[k]。再設b[p] b[p+1]……b[k]這個子序列爲C。
那麼b[p]必定不是C中的最小元素,由於有b[k]<b[p]=a[k]。
而後不妨設b[q]爲C的最小元素。而後咱們把b[p]移到b[k]的位置,獲得序列bb。
若是bb合法的話,那麼咱們就獲得了一個比b優的解,這與b是最優解矛盾。
(由於b[q]的位置前移了一位,咱們要求編號小的儘量靠前)
但bb顯然是合法的。由於在a序列中k以及後面的是合法的,那麼b後面也這麼作必定也是合法的。
因此必定不存在某個k,使得a[k]!=b[k]。也就是說a=b。
因此算法正確性得證。
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int MAXN=100000+50;
 4 int n,m,in;
 5 vector<int> E[MAXN];
 6 priority_queue<int> que;
 7 int ans[MAXN],inn[MAXN];
 8 
 9 void init()
10 {
11     scanf("%d%d",&n,&m);
12     memset(inn,0,sizeof(inn));
13     for (int i=1;i<=n;i++) vector<int>().swap(E[i]);
14     for (int i=0;i<m;i++)
15     {
16         int u,v;
17         scanf("%d%d",&u,&v);
18         inn[u]++;
19         E[v].push_back(u);
20     }
21 }
22 
23 void solve()
24 {
25     while (!que.empty()) que.pop();
26     ans[0]=0;
27     for (int i=1;i<=n;i++) if (!inn[i]) que.push(i);
28     while (!que.empty())
29     {
30         int u=que.top();que.pop();
31         ans[++ans[0]]=u;
32         for (int i=0;i<E[u].size();i++)
33         {
34             int v=E[u][i];
35             inn[v]--;
36             if (!inn[v]) que.push(v);
37         }
38     }
39     if (ans[0]<n) puts("Impossible!");
40         else 
41         {
42             for (int i=ans[0];i>=1;i--) printf("%d ",ans[i]);
43             printf("\n");
44         }
45 }
46 
47 int main()
48 {
49     int T;
50     scanf("%d",&T);
51     while (T--)
52     {
53         init();
54         solve();
55     }
56     return 0;
57 }
相關文章
相關標籤/搜索