首页 > 代码库 > 【拓扑排序】BZOJ4010-[HNOI2015]菜肴制作

【拓扑排序】BZOJ4010-[HNOI2015]菜肴制作

【题目大意】

是要求N个点的一个拓扑序,且满足以下条件:编号1的位置尽可能靠前,在满足所有限制,编号2的位置尽可能靠前,以此类推。

【思路】

一开始觉得优先队列维护一下拓扑就好了。然而样例告诉我们是不可以的。如果限制条件是:
5 2
4 3

最后出来的会是1-4-3-5-2,而答案应该是1-5-2-4-3。

由此可以发现,如果正向拓扑出来的是“字典序最小”,而不是“编号小的尽可能靠前”。

所以逆向拓扑。

证明……算了困了,改天再纠结吧。丢个链接存个档。?

不妨认为我们这样得到的不是最优解,那么令这样得到的序列为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 }

 

【拓扑排序】BZOJ4010-[HNOI2015]菜肴制作