[HNOI2015]菜肴制作(拓扑排序)
知名美食家小 A被邀请至ATM 大酒店,为其品评菜肴。 ATM 酒店为小 A 准备了 N 道菜肴,酒店按照为菜肴预估的质量从高到低给予1到N的顺序编号,预估质量最高的菜肴编号为1。
由于菜肴之间口味搭配的问题,某些菜肴必须在另一些菜肴之前制作,具体的,一共有 M 条形如”i 号菜肴'必须'先于 j 号菜肴制作“的限制,我们将这样的限制简写为<i,j>。
现在,酒店希望能求出一个最优的菜肴的制作顺序,使得小 A能尽量先吃到质量高的菜肴:
也就是说,
(1)在满足所有限制的前提下,1 号菜肴”尽量“优先制作;
(2)在满足所有限制,1号菜肴”尽量“优先制作的前提下,2号菜肴”尽量“优先制作;
(3)在满足所有限制,1号和2号菜肴”尽量“优先的前提下,3号菜肴”尽量“优先制作;
(4)在满足所有限制,1 号和 2 号和 3 号菜肴”尽量“优先的前提下,4 号菜肴”尽量“优先制作;
(5)以此类推。
例1:共4 道菜肴,两条限制<3,1>、<4,1>,那么制作顺序是 3,4,1,2。
例2:共5道菜肴,两条限制<5,2>、 <4,3>,那么制作顺序是 1,5,2,4,3。
例1里,首先考虑 1,因为有限制<3,1>和<4,1>,所以只有制作完 3 和 4 后才能制作 1,而根据(3),3 号又应”尽量“比 4 号优先,所以当前可确定前三道菜的制作顺序是 3,4,1;接下来考虑2,确定最终的制作顺序是 3,4,1,2。
例 2里,首先制作 1是不违背限制的;接下来考虑 2 时有<5,2>的限制,所以接下来先制作 5 再制作 2;接下来考虑 3 时有<4,3>的限制,所以接下来先制作 4再制作 3,从而最终的顺序是 1,5,2,4,3。 现在你需要求出这个最优的菜肴制作顺序。无解输出”Impossible!“ (不含引号,首字母大写,其余字母小写)
Solution
先考虑暴力的做法,我们从一号点开始枚举,从限制一号点的所有点中找到编号最小的点,在从限制它的点中找编号最小的。。。。以此类推。
于是我们发现这个过程和在反向图上拓扑排序的过程是一样的。
Code
#include<iostream> #include<cstdio> #include<queue> #include<cstring> #define N 100002 using namespace std; int t,n,m,du[N],head[N],tot,x,y,ans[N],top; struct zzh{ int n,to; }e[N]; inline void add(int u,int v){ e[++tot].n=head[u]; e[tot].to=v; head[u]=tot; } priority_queue<int>q; int main(){ scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); memset(head,0,sizeof(head)); memset(du,0,sizeof(du));tot=top=0; for(int i=1;i<=m;++i){ scanf("%d%d",&x,&y); add(y,x); du[x]++; } for(int i=1;i<=n;++i)if(!du[i])q.push(i); while(!q.empty()){ int u=q.top();q.pop();ans[++top]=u; for(int i=head[u];i;i=e[i].n){ int v=e[i].to; if(!--du[v])q.push(v); } } if(top!=n)printf("Impossible!\n"); else{ for(int i=top;i>=1;--i)printf("%d ",ans[i]); printf("\n"); } } return 0; }