HDU 4857逃生(拓扑排序+反向建边)

题目链接点这个!!!
题目大意:
给你几个约束条件,要求谁必须在谁的前面。同时要求编号越小的越要在前面,注意这里不是字典序,注意题目描述!!
思路:
首先这个毫无疑问,这种和优先级相关的基本拓扑排序没跑,但很明显这不是一种优先级了。两种优先级,一种是题目给出的顺序(即边),另一种就是编号顺序,从小到大。起初完全没思路,看了这篇非常nice的博客后懂了点这里!!!!
我就不抄袭他的了,我就单纯说说自己的理解吧。首先我们都知道如果一个点的入度为0那么他肯定是优先级最高的,也就是起点,如果从入度这个方面思考这个题的话,会发现我们不能确定先把那个点从队列中剔除,也就是我们不能确定哪个点后面有1(或2或3依次类推)。所以这样思考是很明显麻烦的。
换一个方向,既然是有向图,没了入度就是出度了。那么思考出度为0的点代表什么,没错,就是终点,既然我们不能确定起点,但是我们可以确定终点,题目要求最后的拓扑序列前面的点尽量小,那么反过来就是后面的点尽量大了,并且我们还可以确定终点。
所以整体思路就确定了。从出度为0的点考虑。大体流程如下:
1.找到所有出度为0的点,放入优先队列(这里用大根堆,因为要让后面的点尽量大)
2.当队列非空时,弹出堆顶元素,放入答案末尾。
3.遍历所有与它相连的点,使它们出度减一。(因为是要遍历所有到他的点,而不是他到的点,所以在建图时要反向建边就可以解决这个问题)。
4.当遇到出度为0的点时再次把点放入优先队列,重复上述过程。
最终所得即为答案所求。

#include<iostream>
#include<queue>
#include<vector>
#include<cstdio>
#include<map>
#include<string>
#include<functional>
#include<algorithm>
#include<cstring>
#define lk (k<<1)
#define rk (k<<1|1)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=3e4+10,M=1e5+10;
int du[N],n,m,ans[N];
struct edge
{
    int to,next;
}e[M];
int head[N],cnt=0;
void addedge(int u,int v)
{
    e[++cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt;
}
void topsort()
{
    priority_queue<int>q;
    for(int i=1;i<=n;i++) if(!du[i]) q.push(i);
    int len=n;
    while(!q.empty())
    {
        int now=q.top();
        q.pop();
        ans[len--]=now;
        for(int i=head[now];i;i=e[i].next)
        {
            int to=e[i].to;
            du[to]--;
            if(!du[to]) q.push(to);
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        memset(head,0,sizeof(head));
        memset(du,0,sizeof(du));
        cnt=0;
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            addedge(y,x);
            du[x]++;
        }
        topsort();
        for(int i=1;i<=n;i++)
        {
            printf("%d",ans[i]);
            if(i!=n) printf(" ");
        }
        printf("\n");
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值