HDU-4857逃生(反向拓扑排序)(重点是思想)

来源:互联网 发布:java流行框架2017 编辑:程序博客网 时间:2024/05/21 06:13


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


这道题一看也许你就知道是拓扑排序,但是这个和普通 的拓扑有些不一样!!


先来看看拓扑的讲解(做此题的时候我还不会拓扑)、

拓扑排序的原理及其实现


刚开始我就是用普通的拓扑排序+优先队列

让队列中最小的先出队~

当时我觉得没有什么不对!

附上代码:

#include<stdio.h>#include<queue>#include<string.h>#include<algorithm>#include<iostream>using namespace std;const int maxn=100000+10;const int MAXN=30000+10;int u[maxn],v[maxn];int input[MAXN],list[MAXN];int first[MAXN],NEXT[maxn],vis[MAXN];bool map0[MAXN][MAXN];int main(){    int T;    scanf("%d",&T);    while(T--)    {        int n,m;        scanf("%d%d",&n,&m);        memset(input,0,sizeof(input));        memset(first,-1,sizeof(first));        memset(vis,0,sizeof(vis));        for(int i=1; i<=m; i++)        {            scanf("%d%d",&u[i],&v[i]);            input[v[i]]++;            NEXT[i]=first[u[i]];            first[u[i]]=i;        }//        for(int i=1;i<=n;i++)//        {//            printf("%d ",input[i]);//        }//        printf("\n");        priority_queue< int,vector<int>,greater<int> >q;        for(int i=1; i<=n; i++) //先把入度为1先扔进去        {            if(input[i]==0)            {                q.push(i);            }        }        int step=0;        while(!q.empty())        {            int top=q.top();            q.pop();            int k=first[top];            while(k!=-1)            {                input[v[k]]--;                if(input[v[k]]==0)                {//                    printf("%d*-*\n",v[k]);                    q.push(v[k]);                }                k=NEXT[k];            }            list[++step]=top;        }        for(int i=1; i<=step; i++)        {            printf("%d ",list[i]);        }        printf("\n");    }}

天真的以为我一直先输出的是队列中最小的数,这么排出顺序一定是小的在钱,大的比较靠后


然而有一组数据

1
10 9
6 4
4 1
3 9
9 2
5 7
7 8
1 10
2 10
8 10

上面代码输出是  :3 5 6 4 1 7 8 9 2 10

但是发现1不是最靠前的

题意是如果有多种情况,必须先考虑1先走,然后才考虑2走,然后考虑3

就是如果没说1在2后面(直接或者间接的关系表面1在2后面),那么1就要在2前面

那么这样你就会发现这个输出是错误的!!

正确的应该是 6 4 1 3 9 2 5 7 8 10

这样1在2前面而题意说3在2前面,这组输出就是我们要的答案

然而如何得到这组答案这是思维的重点

首先看看为什么会出现这种错误??

其实根源就是优先队列的毛病

出队的时机不对,如果把握出队的时机?

我们要最小的在前面(最小的先出)然而在队列中最小的是时刻在变!!

比如1还没有进入队列的时候2是最小的,当1进入队列后1才是最小的!!

这样导致一个结果是,如果2先进队列了,那么2的最小的,先出去了!!然后1进来了

那么根本无法保证1和2的出队顺序是一直是1在2前面

然而我们换个思路,这个问题是出队出的早了!!

那么我们让出队出晚点,让大的数先出队!!!

相对的,小的数就会留在队列中!!(越小越留到最后)

这样1和2肯定能同时留在队列中,而1肯定要比2后出

这样保证了1和2的绝对出栈顺序,然后反过来输出就OK了!!

#include<stdio.h>#include<queue>#include<string.h>#include<algorithm>#include<iostream>using namespace std;const int maxn=100000+10;const int MAXN=30000+10;int u[maxn],v[maxn];int input[MAXN],list[MAXN];int first[MAXN],NEXT[maxn],vis[MAXN];bool map0[MAXN][MAXN];int main(){    int T;    scanf("%d",&T);    while(T--)    {        int n,m;        scanf("%d%d",&n,&m);        memset(input,0,sizeof(input));        memset(first,-1,sizeof(first));        memset(vis,0,sizeof(vis));        for(int i=1; i<=m; i++)        {            scanf("%d%d",&u[i],&v[i]);            input[u[i]]++;            NEXT[i]=first[v[i]];            first[v[i]]=i;        }//        for(int i=1;i<=n;i++)//        {//            printf("%d ",input[i]);//        }//        printf("\n");        priority_queue<int>q;        for(int i=1; i<=n; i++) //先把出度为0先扔进去        {            if(input[i]==0)            {                q.push(i);            }        }        int step=0;        while(!q.empty())        {            int top=q.top();            q.pop();            int k=first[top];            while(k!=-1)            {                input[u[k]]--;                if(input[u[k]]==0)                {//                    printf("%d*-*\n",v[k]);                    q.push(u[k]);                }                k=NEXT[k];            }            list[++step]=top;        }        for(int i=step; i>=2; i--)        {            printf("%d ",list[i]);        }        printf("%d\n",list[1]);    }}