一道明目张胆的拓扑排序的题目
首先,由于题目给出“某些菜肴必须在另一些菜肴之前制作”
这一条件,所以这道题可以用拓扑去做。
其次,根据题目给出的“最优的菜肴制作顺序”的定义,我们自然而然的想到题目是让我们求字典序最小的拓扑序。
然而,这样做事错误的。
出题人可以轻松把你卡掉,比如样例的第三组数据,答案是1 5 2 4 3
,但是如果你是求的字典序最小的拓扑序,那么你求出的结果将会是1 4 3 5 2
。很显然,2的位置靠后了。
既然求最小的字典序行不通,那么我们是不是可以反过来想呢?
由于题目要求你把小的数尽量往前放,所以答案不一定是最小的字典序。但是,把小的数尽量往前方,那么大的数自然会尽量往后,这样一来,如果把拓扑序反过来,那么字典序最大的反拓扑序就一定是最优解。于是,我们可以建一个返图去求字典序最大的拓扑序,最后再倒序输出。 tip:为了优化时间复杂度,我们可以用堆去进行拓扑排序。 附一下代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#define ll long long
#define INF 0x7fffffff
using namespace std;
priority_queue<int>que;
int t,n,m,x,cnt,d[100005],k[100005],ans[100005];
struct Edge{
int to,next;
}edge[100005];
struct node{
int x,y;
}u[100005];
void add(int x,int y)
{
edge[++cnt].to = y;
edge[cnt].next = d[x];
d[x] = cnt;
k[y] ++;
}
int mysort(node a,node b) {return a.y < b.y;}
void clean()
{
while(!que.empty()) que.pop();
}
void work()
{
for(register int i = 1; i <= n; i++) if(k[i] == 0) que.push(i);
while(!que.empty())
{
x = que.top();
ans[++cnt] = x;
que.pop();
for(register int i = d[x]; edge[i].to != 0; i = edge[i].next)
{
k[edge[i].to] –;
if(k[edge[i].to] == 0) que.push(edge[i].to);
}
}
}
int main()
{
scanf(“%d”,&t);
for(register int a = 1; a <= t; a++)
{
cnt = 0;
memset(d,0,sizeof(d));
memset(k,0,sizeof(k));
clean();
scanf(“%d%d”,&n,&m);
for(register int i = 1; i <= m; i++) scanf(“%d%d”,&u[i].x,&u[i].y);
for(register int i = 1; i <= m; i++) add(u[i].y,u[i].x);
cnt = 0;
work();
if(cnt < n) printf(“Impossible!”);
else
for(register int i = n; i >= 1; i–) printf(“%d “,ans[i]);
printf(“\n”);
}
return 0;
}