现在的位置: 首页 > 综合 > 正文

【拓扑排序】最小拓扑序 topsort

2014年01月23日 ⁄ 综合 ⁄ 共 1508字 ⁄ 字号 评论关闭

最小拓扑序

topsort.pas/c/cpp

1s/64MB

 

【题目描述】

给一个有向无环图,求其字典序最小的拓扑序。

一个拓扑序被认为字典序{pi}最小当且仅当对于任何其他拓扑序{qi},均存在正整数k,使得对于所有i<k有pi=qi且pk<qk。

【输入】

输入第一行包含两个数n, m分别表示有向无环图的点数和边数。

接下来m行,每行两个数ai, bi,表示图中存在一条ai指向bi的有向边。

【输出】

输出n个数,每个数用空格隔开,表示该图的拓扑序。

【输入样例】

3 1

2 1

【输出样例】

2 1 3

 

【样例解释】

2, 1, 3

2, 3, 1

3, 2, 1

均为合法拓扑序,但是2, 1, 3为最小字典序的拓扑序。

 

【数据规模】

50%的数据满足:n<=1000

100%的数据满足:1<=n<=100000, 1<=m<=100000, 1<=ai,bi<=n

保证数据至少存在一种合法的拓扑序列

完成情况(cena测评)

这一题求拓扑序很容易,但是题目多了一个要求,字典序最小

其实这个要求我们在无形中已经达到了,先看看O(N*N)的算法

for(int i=1;i<=n;i++)
	if(deg[i]==0)
	{
		printf("%d ",i);
		for(int j=1;j<=n;j++)
			if(map[i][j]) deg[j]--;
	}

这样输出来的就肯定是按照字典序的!

因为我们只要保证每次找入度为0的点的序号最小,那么我们就能保证整个字典序最小

但是这样是显然要超时的,我们可以用队列优化(栈什么的也可以)

先看看上面的代码,j 循环是不能省掉的(因为必须要删边)

但是我们看看最外层的 i 循环,是找入度为 0 的点,那么我们的优化就要在这上面做文章了

我们在队列里存下所有入度为0的点,每一次就取队首,然后一个循环删边即可,这样就可以搞掉外面的一重 i 循环了

至于保证字典序最小,很简单,把队列改成优先队列(堆)就ok!

C++ AC Code

/*http://blog.csdn.net/jiangzh7
By Jiangzh*/
#include<cstdio>
#include<queue>
using namespace std;
const int N=100000+10;
const int inf=0x3f3f3f3f;

int n,m;
struct link{int y;link *next;}*head[N];
int deg[N];
priority_queue<int,vector<int>,greater<int> > Q;

void inlink(int x,int y)
{
	link *node=new link;
	node->y=y;node->next=head[x];
	head[x]=node;
}

void read()
{
	freopen("topsort.in","r",stdin);
	freopen("topsort.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		inlink(x,y);deg[y]++;
	}
}

void work()
{
	for(int i=1;i<=n;i++) if(deg[i]==0) Q.push(i);
	while(!Q.empty())
	{
		int j=Q.top();Q.pop();
		printf("%d ",j);deg[j]=inf;
		for(link *node=head[j];node;node=node->next)
		{
			deg[node->y]--;
			if(!deg[node->y]) Q.push(node->y);
		}
	}
}
		
int main()
{
	read();
	work();
	return 0;
}

抱歉!评论已关闭.