zoukankan      html  css  js  c++  java
  • HDU 4857 逃生 【拓扑排序+反向建图+优先队列】

    逃生
    Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 6686 Accepted Submission(s): 1958

    Problem Description
    糟糕的事情发生啦,现在大家都忙着逃命。但是逃命的通道很窄,大家只能排成一行。

    现在有n个人,从1标号到n。同时有一些奇怪的约束条件,每个都形如:a必须在b之前。
    同时,社会是不平等的,这些人有的穷有的富。1号最富,2号第二富,以此类推。有钱人就贿赂负责人,所以他们有一些好处。

    负责人现在可以安排大家排队的顺序,由于收了好处,所以他要让1号尽量靠前,如果此时还有多种情况,就再让2号尽量靠前,如果还有多种情况,就让3号尽量靠前,以此类推。

    那么你就要安排大家的顺序。我们保证一定有解。

    Input
    第一行一个整数T(1 <= T <= 5),表示测试数据的个数。
    然后对于每个测试数据,第一行有两个整数n(1 <= n <= 30000)和m(1 <= m <= 100000),分别表示人数和约束的个数。

    然后m行,每行两个整数a和b,表示有一个约束a号必须在b号之前。a和b必然不同。

    Output
    对每个测试数据,输出一行排队的顺序,用空格隔开。

    Sample Input
    1
    5 10
    3 5
    1 4
    2 5
    1 2
    3 4
    1 4
    2 3
    1 5
    3 5
    1 2

    Sample Output
    1 2 3 4 5

    Author
    CLJ
    【题意】:有N个人,M个优先级a,b表示a优先于b,并且每个人有个编号的优先级,输出顺序。

    拓扑排序,因为需要尽可能让编号较小的在前面,比如有如下约束

    1->4->2 5->3->2

    如果我们考虑正向建图的话得出的拓扑序列为 1 4 5 3 2

    但是实际上,在图中 1 4 与 5 3 之间并没有约束,并且负责人在排序的时候会先去考虑 3 号的位置,于是有另一种结果 1 5 3 4 2 。

    可见,第二种结果才是正确的,因为较小的编号总是优先选择靠前。

    因为正向建图求拓扑序列的过程中,编号较小的点可能会被更大的点挡住,于是我们采用逆向建图的方法,然后用大顶堆维护,把编号较大的点先选出来,最后反转数组即可。

     题意大抽象后概是这样的:给定一个有向图,将其按线性优先级输出。题目大致一扫,可以明白是个赤果果的拓扑排序。以前学过一点拓扑排序,后来给忘掉
    
    了。拓扑排序就是解决对有向图的点进行线性排序的相关问题。假设给定一标记优先级的有向图,那么每个有向边通向的点的入度都要+1,这样的标记后,入度为
    
    0的点就可以看成是优先级最高的点(因为没有点在它前面去连接它)。把此点取出,与这个点相连的其他点的边也就全部都要消掉了,同时将它们的入度减去1;
    
    这样会产生新的入度为0的点,那么这个点就是剩下的点中的优先级最高的点,重复上述的更新过程即可。此题有坑,可能会有多个拓扑环;还有此题要求输出的点
    
    如果没有拓扑关系则按照小编号在前的方式排列。看到这个条件第一反应是在取优先级高的点的过程中用优先队列去写,先出队列的就是编号较小的,这么想似乎没
    
    错。但是后来debug了下,假如有一有向图关系是这样的:6->3->1;5->4->2;这样的话按照最小堆来进行拓扑排序会出这样的结果:5 4 2 6 3 1;但是实际上
    
    我们可以把6 3 1 这个拓扑环放在前面,生成6 3 1 5 4 2这样的线性序列。究其原因,我们优先队列先弹出的是编号较小的符合条件的点,但实际上这个点只能算
    
    是该拓扑环排序后的头结点,我们想要保证让小号的全部尽可能地放在前面,我们需要保证拓扑环的尾部的小号也在前面。这样来想一下可以发现先出编号小的是
    
    不完善的。于是我们可以考虑逆向思维,先弹出序号大的,那么这样先出队列的就是编号最大的;但是我们有需要编号最大的在最后输出,换句话说就是编号最大的
    
    是作为优先级最低的;那么我们把有向图也给逆向储存,这样按优先级条件弹出的顺序才会一致(优先弹出优先级最低的)。大致思路明白后代码实现就很简单了。
    

    #include<cstdio>
    #include<string>
    #include<cstdlib>
    #include<cmath>
    #include<iostream>
    #include<cstring>
    #include<set>
    #include<queue>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<cctype>
    #include<stack>
    #include<sstream>
    #include<list>
    #include<assert.h>
    #include<bitset>
    #include<numeric>
    #define debug() puts("++++")
    #define gcd(a,b) __gcd(a,b)
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define fi first
    #define se second
    #define pb push_back
    #define sqr(x) ((x)*(x))
    #define ms(a,b) memset(a,b,sizeof(a))
    #define sz size()
    #define be begin()
    #define pu push_up
    #define pd push_down
    #define cl clear()
    #define lowbit(x) -x&x
    #define all 1,n,1
    #define rep(i,n,x) for(int i=(x); i<(n); i++)
    #define in freopen("in.in","r",stdin)
    #define out freopen("out.out","w",stdout)
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef pair<int,int> P;
    const int INF = 0x3f3f3f3f;
    const LL LNF = 1e18;
    const int maxn = 1e5 + 20;
    const int maxm = 1e6 + 10;
    const double PI = acos(-1.0);
    const double eps = 1e-8;
    const int dx[] = {-1,1,0,0,1,1,-1,-1};
    const int dy[] = {0,0,1,-1,1,-1,1,-1};
    const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    
    int n,m,num,ok,u,v;
    /*
    4—>2
    5—>1—>3
    正常情况下字典序最小的拓扑序应该是:4 2 5 1 3
    这种规则下,则应该是: 5 1 4 2 3
    */
    vector<int> G[maxn];
    int ans[maxn];
    
    char s[10];
    int inDeg[maxn],x;
    
    void topSort()
    {
        priority_queue<int> q; //优先队列默认的是数据大的优先级高
        int num=0;
        while(!q.empty()) q.pop();
        for(int i=1;i<=n;i++) if(!inDeg[i]) q.push(i);
        while(!q.empty())
        {
            int now = q.top();
            q.pop();
            ans[++num]=now;
            for(int i=0; i<G[now].size(); i++)
            {
                int nxt = G[now][i];
                if(--inDeg[nxt] == 0) q.push(nxt);
            }
        }
    
    }
    
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
            ms(inDeg,0);
            ms(ans,0);
            for(int i=1;i<=n;i++) G[i].clear();
            for(int i=1;i<=m;i++)
            {
                scanf("%d%d",&u,&v);
                G[v].push_back(u);
                inDeg[u]++;
            }
            topSort();
            for(int i=n;i>=1;i--)
            {
                if(i!=1) printf("%d ",ans[i]);
                else printf("%d
    ",ans[i]);
            }
        }
        return 0;
    }
    /*
    1
    5 10
    3 5
    1 4
    2 5
    1 2
    3 4
    1 4
    2 3
    1 5
    3 5
    1 2
    */
    
    
  • 相关阅读:
    装饰器
    函数对象与闭包
    名称空间与作用域
    函数的参数
    函数的基本使用
    ${}与#{}的区别
    thymeleaf之日期格式化
    template might not exist or might not be accessible by any of the configured Template Resolvers
    springboot使用@Scheduled之cron表达式详解
    自定义springboot项目启动图案
  • 原文地址:https://www.cnblogs.com/Roni-i/p/9179375.html
Copyright © 2011-2022 走看看