Luogu P3243 [HNOI2015]菜肴制作
这道题的正解贪心思路我看了题解....
给出点对的先后次序,很容易想到拓扑排序。
但是如果按字典序排序,就会遇到较小的就放在前面,
可以举出反例:4种菜肴,限制为<2,4><3,1>,
那么字典序最小的是2,3,1,4,但正解应为3,1,2,4.
因为按字典序排序只能按前一个点排,不能知道后一个点的大小。
通过观察发现,想让最小的尽早出现,就是要让较大的尽可能晚出现。
而可以放在后面的点是确定的。
那么反向连边,最后输出拓扑的倒序即可。
注意,不满足条件的情况除了开始就没有入度为0的点外,还有可能有部分几个点形成闭环。
所以最后要判断排出顺序的点数是否与总点数相等。
代码如下
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #define MogeKo qwq #include<queue> using namespace std; const int maxn = 1e5+10; int t,n,m,x,y,cnt,num; int a[maxn],inn[maxn]; int head[maxn],to[maxn],nxt[maxn]; void add(int x,int y) { to[++cnt] = y; nxt[cnt] = head[x]; head[x] = cnt; } void init() { cnt = num = 0; memset(head,0,sizeof(head)); memset(to,0,sizeof(to)); memset(nxt,0,sizeof(nxt)); memset(inn,0,sizeof(inn)); } void topo() { priority_queue <int> q; for(int i = 1; i <= n; i++) if(!inn[i]) q.push(i); if(q.empty()) { printf("Impossible!\n"); return; } while(!q.empty()) { int u = q.top(); q.pop(); a[++num] = u; if(!head[u]) continue; for(int i = head[u]; i; i = nxt[i]) { int v = to[i]; inn[v]--; if(inn[v] == 0) q.push(v); } } if(num < n) { printf("Impossible!\n"); return; } for(int i = n; i >= 1; i--) printf("%d ",a[i]); printf("\n"); } int main() { scanf("%d",&t); while(t--) { init(); scanf("%d%d",&n,&m); for(int i = 1; i <= m; i++) { scanf("%d%d",&x,&y); add(y,x); inn[x]++; } topo(); } return 0; }