拓扑编号

版权声明:本文为博主原创文章,转载需标注来源。 https://blog.csdn.net/Code_Mart/article/details/78270384

描述

H国有n个城市,城市与城市之间有m条单向道路,满足任何城市不能通过某条路径回到自己。
现在国王想给城市重新编号,令第i个城市的新的编号为a[i],满足所有城市的新的编号都互不相同,并且编号为[1,n]之间的整数。国王认为一个编号方案是优美的当且仅当对于任意的两个城市i,j,如果i能够到达j,那么应当a[i] < a[j]。
优美的编号方案有很多种,国王希望使1号城市的编号尽可能小,在此前提下,使得2号城市的编号尽可能小…依此类推。

格式

输入格式

第一行读入n,m,表示n个城市,m条有向路径。
接下来读入m行,每行两个整数:x,y
表示第x个城市到第y个城市有一条有向路径。

输出格式

输出一行:n个整数
第i个整数表示第i个城市的新编号a[i],输出应保证是一个关于1到n的排列。

样例1

样例输入1

5 4
4 1
1 3
5 3
2 5

样例输出1

2 3 5 1 4

限制

每个测试点1s

提示

30%的测试点满足:n <= 10, m <= 10
70%的测试点满足:n <= 1000, m <= 10000
100%的测试点满足:n <= 100000, m <= 200000
输入数据可能有重边,可能不连通,但保证是有向无环图。

来源

Topcoder

题解:

初看此题,很明显是拓扑排序的思想。首先,由题目可知数据所组成的图为DAG(有向无环图);其次,由“对于任意的两个城市i,j,如果i能够到达j,那么应当a[i] < a[j]”可知,在所得序列中,每一个顶点都不会通过原图中的边指向任何前驱顶点。

采用普通拓扑排序的算法,则每次所选择的顶点都应是当前indegree(入度)为零且编号最小的顶点。但是,很明显,如果这样去写,样例都过不了。

换种角度去思考,采用逆向拓扑排序+优先队列,即读取输入数据时,所建立的边都是反向边,每次入队的顶点都是当前indegree(入度)为零的顶点,每次出队的顶点自然是在队列中编号最大的顶点。

证明:

假设通过逆向拓扑排序所得到的序列为A,而当前输入数据的最优解为B。
如果A并非最优解,则从右到左必然存在第一个不同的结果,即A[K]!=B[K],那么存在P < K,使得B[P]=A[K].
记序列C为B[P],B[P+1],…,B[K],即A[K],B[P+1],…,B[K].
那么,序列C必然不是最优解,因为假设C中编号最小的顶点为C[I],则
B[P+1],B[P+2],..,B[I],A[K],B[I+1],…,B[K]相较于C而言更优,因为B[I]的排名更小了。
这时候,要思考如何保证移动A[K]是合法的?毕竟,如果A[K]是B[P+1]的前驱的话,我们就不能去移动它。
反过来看A、B,因为K是A、B从左往右第一个不同数据的编号,所以P+1<=K。如果A[K]是B[P+1]的前驱,则K应当小于P+1。发生矛盾,故A[K]不是B[P+1]的前驱。

Code:

#include <iostream>
#include <memory.h>
#include <stdio.h>
#include <queue>
using namespace std;
#define maxn 100000
#define maxm 200000
struct edge
{
    int to;
    int next;
};
edge map[maxm+5];
int indegree[maxn+5] = {0};
int head[maxn+5];
int ans[maxn+5] = {0};
int main()
{
    memset(head, -1, sizeof(head));
    int N,M;
    scanf("%d%d",&N,&M);
    int index = N;
    for(int m = 0;m<M;m++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        map[m].to = x;
        map[m].next = head[y];
        head[y] = m;
        indegree[x]++;
    }
    priority_queue<int> Queue;
    for(int i=1; i<=N; i++)
        if(indegree[i] == 0) Queue.push(i);
    while(!Queue.empty())
    {
        int top;
        top = Queue.top();
        Queue.pop();
        ans[top] = index--;
        indegree[top] = -1;
        for(int i = head[top]; i != -1; i = map[i].next)
            if(!--indegree[map[i].to]) Queue.push(map[i].to);
    }
    printf("%d",ans[1]);
    for (int n = 2;n<=N;n++)
        printf(" %d",ans[n]);
    printf("\n");

    return 0;
}

猜你喜欢

转载自blog.csdn.net/Code_Mart/article/details/78270384