题意:n个结点,m个拓扑关系(a,b)表示a必须排在b前面,在满足m个拓扑关系关系的前提下使得小的结点尽可能的排在前面。也就是说我们现在要从1号结点开始考虑,如果要排1号结点,根据拓扑关系,首先必须排哪些结点,如果排好了1号结点,则继续考虑2号结点 ,3号结点。。
我们先看两个例子:
存在拓扑关系:
5 -> 3 -> 1
6 -> 4 -> 2
直接拓扑排序的结果是 5 3 1 6 4 2,结果是正确的(1号尽可能的在前面了) ,看起来好像直接拓扑排序就可以了,但是为什么提交后却wa了呢,别急,我们再看一个例子:
6 -> 3 -> 1
5 -> 4 -> 2
直接拓扑排序的结果是:5 4 2 6 3 1 ,结果是错误的,因为我们可以把1号安排到更前面的位置 即:6 3 1 5 4 2(正确答案)。
看到这里应该就知道为什么直接拓扑排序不行了吧,我们分析一下为什么会出现这样的状况,对于多条弧或者边,我们是先删除弧尾结点比较小的结点即(5号结点比6号结点小,先删除以该点为尾的弧),而这也正是问题所在,我们希望的是优先删除弧头比较小的弧的(1号结点比2号结点小,因优先删除以1号为头的弧)。
好了,问题找到了,现在我们在来想如何解决问题,我们可以尝试一下逆向思维,即我们先考虑哪些点应该靠后释放,这样的话我们就可以把拓扑关系反过来(即弧头和弧尾调换),然后做拓扑排序,然后我们可以根据原来的弧头(现在变成弧尾)的大小来释放结点,由于现在的问题变成哪些最后释放,那么就应该优先考虑弧尾(原来的弧头)比较大的,可以通过优先队列来解决。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<queue>
#include<utility>
using namespace std;
const int maxn = 31000;
pair<int,int> a[maxn * 4];
int h[maxn];
int in[maxn];
int ans[maxn],cnt;
int main()
{
int tt;
scanf("%d",&tt);
int x,y;
while(tt--)
{
int n,m;
memset(h,0,sizeof(h));
memset(in,0,sizeof(in));
scanf("%d%d",&n,&m);
cnt = 0;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&y,&x);
if(h[x] == 0){
h[x] = i;
a[i].first = y;
a[i].second = 0;
in[y]++;
}else{
int t = h[x];
h[x] = i;
a[i].first = y;
a[i].second = t;
in[y]++;
}
}
priority_queue<int,vector<int>, less<int> > que;
for(int i=1;i<=n;i++)
if(in[i] == 0) que.push(i);
while( !que.empty()){
int top = que.top();
que.pop();
ans[cnt++] = top;
for(int i = h[top];i!=0;i = a[i].second){
in[a[i].first]--;
if(in[a[i].first] == 0)
que.push(a[i].first);
}
}
for(int i=cnt -1 ;i > 0;i--)
printf("%d ",ans[i]);
printf("%d\n",ans[0]);
}
}