【拓扑排序】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 }
相关文章
相关标签/搜索