反向拓扑排序 HDU 4857 逃生

题意: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]);

    }
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值