描述
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;
}