HDU-4857 逃生 【反向拓扑排序模板题+死环判定】

C - 逃生

Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u
Submit

Status
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


1.题意:主要是这句话:(由于收了好处,所以他要让1号尽量靠前,如果此时还有多种情况,就再让2号尽量靠前,如果还有多种情况,就让3号尽量靠前),说的是这个序列的先后原则:在各种约束条件之下,优先考虑1,在考虑2,也就是说,如果1,2之间没有约束条件的话,1必须在2的前面,如1,2,3这三个人有约束条件3必须在1之前,那么序列就是3,1,2;
2.思路:逆向思维:直接处理要考虑其他约束条件,从图上看就是将有向图的所有边的指向改变,将得到的图看成新图,再用拓扑排序,然后逆向输出。从题意的分析来看,如果要使用正向的拓扑排序,那么我们在选择第一个点的时候还要考虑它后面约束的人里面有没有1,如第一次选我们要将对1有约束的人都排完,然后拍1,确保1的位置尽量靠前,继续排对2有约束的人,然后在排2,依次类推,直到所有的人都排完为止。这就有点麻烦,我们在写拓扑排序时要转换角度,所有事情都是这样,你对我有约束,我就对你有反约束,作用力和反作用力嘛,正向麻烦时就看看逆向时会怎样。此题逆向就是先选哪个人最晚走,一定时图中的0入度点,如果遇到多个0入度点,就选序号大的,从这里看我们可以用优先队列得到这个序列,然后存在栈中,就解决了;
3.失误:第一遍没有理解那句话,根本就没考虑那么多条件约束,就挂了。
4.代码如下:


邻接表+优先队列+栈:

#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
#include<functional>
using namespace std;

const int MAXN=1e6+10;
int head[MAXN],ideg[MAXN],ord[MAXN];
int en,cnt;

struct Edge{
    int to;
    int next;
}edge[MAXN];

void init()
{
    memset(head,-1,sizeof(head)); en=0;
}

void addedge(int u,int v)
{
    Edge E={v,head[u]};
    edge[en]=E;
    head[u]=en++;
} 

void topo(int n)
{
    priority_queue<int> que;  stack<int> sta;
    for(int i=1;i<=n;++i)
    {
        if(!ideg[i])
        {
            que.push(i); ideg[i]=-1;
        }
    }
    cnt=0;
    while(!que.empty())
    {
        int now=que.top(); que.pop(); //ord[++cnt]=now;
        sta.push(now);
        for(int i=head[now];i!=-1;i=edge[i].next)
        {
            int go=edge[i].to;
            if(ideg[go]>0)  --ideg[go];//若有重边 临界表在此处已经对度数可以重复减 
            if(!ideg[go]) 
            {
                que.push(go); ideg[go]=-1;
            }
        }
     } 
     while(sta.size()>1)
     {
         printf("%d ",sta.top()); sta.pop();
     }
     printf("%d\n",sta.top()); 
     sta.pop();//可以不加 
}

int main()
{
    int t,i,u,v,n,m;
    scanf("%d",&t);
    while(t--)
    {
        init(); memset(ideg,0,sizeof(ideg));//不初始化也可能造成死循环 没有结束 
        scanf("%d %d",&n,&m);
        for(i=1;i<=m;++i)
        {
            scanf("%d %d",&u,&v);
            addedge(v,u);//临界表对于度数 可以不判重边 
            ++ideg[u];
        }
        topo(n);
//      for(i=cnt;i>=1;--i)
//      {
//          if(i<cnt) printf(" ");
//          printf("%d",ord[i]);
//      }
//      printf("\n"); 
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值