【拓扑排序】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 }