反向拓扑排序 HDU 4857 逃生

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

为什么要反向拓扑排序呢?举个例子:6 -> 3 -> 1    和     5 -> 4 -> 2 。直接拓扑排序的结果是:5 4 2 6 3 1 ,结果是错误的,因为我们可以把1号安排到更前面的位置 即:6 3 1 5 4 2(正确答案)。 

所以我们用逆向思维思考一下先考虑哪些点应该靠后释放,这样的话我们就可以把拓扑关系反过来(即弧头和弧尾调换),然后做拓扑排序,然后我们可以根据原来的弧头(现在变成弧尾)的大小来释放结点,由于现在的问题变成哪些最后释放,那么就应该优先考虑弧尾(原来的弧头)比较大的,可以通过优先队列来解决。

#include<bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 110
#define qx std::ios::sync_with_stdio(false)
#define N 2100
using namespace std;
int in[31000],a,b,m,n;
vector<int> v[30100];
int main(){
    int t;qx;//cin太慢会超时
    cin>>t;
    while(t--){
        cin>>n>>m;
        for(int i=1;i<=n;i++)
			v[i].clear();
        memset(in,0,sizeof in);
        vector<int> ans;
        priority_queue<int>q;

        for(int i=0;i<m;i++){
            cin>>a>>b;
			v[b].push_back(a);
			in[a]++;
        }
        for(int i=1;i<=n;i++){
            if(in[i]==0){
                q.push(i);
            }
        }
        while(!q.empty()){
            int p=q.top();q.pop();
            ans.push_back(p);
            for(int i=0;i<v[p].size();i++){
                in[v[p][i]]--;
                if(in[v[p][i]]==0)
                    q.push(v[p][i]);
            }
        }
        for(int i=ans.size()-1;i>0;i--)
            cout<<ans[i]<<" ";
        cout<<ans[0]<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Endeavor_G/article/details/84837263