拓扑排序总结+例题

步骤:1.找到第一个入度为零的点。
2.将这个点删除(将这个点的入度变为-1)
3.与这个点相连的所有点的入度减1

例题:

1.确定比赛名次 HDU - 1285
hdu-1285
有N个比赛队(1<=N<=500),编号依次为1,2,3,。。。。,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。
Input
输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示队伍的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即P1队赢了P2队。
Output
给出一个符合要求的排名。输出时队伍号之间有空格,最后一名后面没有空格。

其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
Sample Input
4 3
1 2
2 3
4 3
Sample Output
1 2 4 3
经典拓扑排序,输的人的入度加一。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m;
int in[505],ans[505];
int g[505][505]; 
void tp(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(g[i][j]){
				in[j]++;
			}
		}
	}
	for(int i=1;i<=n;i++){
		int k=1;
		while(in[k])k++;
		ans[i]=k;//找到第一个入度为0的点 
		in[k]=-1;//将这个点删除 
		for(int j=1;j<=n;j++){
			if(g[k][j])in[j]--;
		} 
	}
}
int main(){
	while(~scanf("%d%d",&n,&m)){
		memset(in,0,sizeof(in));
		memset(ans,0,sizeof(ans));
		memset(g,0,sizeof(g));
		int x,y;
		for(int i=1;i<=m;i++){
			scanf("%d%d",&x,&y);
			g[x][y]=1;
		}
		tp();
		for(int i=1;i<n;i++){
			printf("%d ",ans[i]);
		}
		printf("%d\n",ans[n]);
	}
	return 0;
} 

2.逃生 HDU - 4857
链接
糟糕的事情发生啦,现在大家都忙着逃命。但是逃命的通道很窄,大家只能排成一行。

现在有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

队列优化的拓扑排序。
因为要求按字典序排序,所以采用优先队列

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
priority_queue<int> q;
vector<int>edge[30010],ans;
int in[30010];
int t,n,m;
void solve(){
	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<edge[p].size();i++){
			int v=edge[p][i];
			in[v]--;
			if(in[v]==0)q.push(v);
		}
	}
	for(int i=ans.size()-1;i>0;i--)printf("%d ",ans[i]);
	printf("%d\n",ans[0]);
}
int main(){
	int a,b;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++){
			edge[i].clear();
			in[i]=0;
		}
		while(!q.empty())q.pop();
		ans.clear();
		while(m--){
			scanf("%d%d",&a,&b);
			edge[b].push_back(a);
			in[a]++;
		}
		solve();
	}
	return 0;
}

3.Legal or Not HDU - 3342
链接
ACM-DIY is a large QQ group where many excellent acmers get together. It is so harmonious that just like a big family. Every day,many “holy cows” like HH, hh, AC, ZT, lcc, BF, Qinz and so on chat on-line to exchange their ideas. When someone has questions, many warm-hearted cows like Lost will come to help. Then the one being helped will call Lost “master”, and Lost will have a nice “prentice”. By and by, there are many pairs of “master and prentice”. But then problem occurs: there are too many masters and too many prentices, how can we know whether it is legal or not?

We all know a master can have many prentices and a prentice may have a lot of masters too, it’s legal. Nevertheless,some cows are not so honest, they hold illegal relationship. Take HH and 3xian for instant, HH is 3xian’s master and, at the same time, 3xian is HH’s master,which is quite illegal! To avoid this,please help us to judge whether their relationship is legal or not.

Please note that the “master and prentice” relation is transitive. It means that if A is B’s master ans B is C’s master, then A is C’s master.
Input
The input consists of several test cases. For each case, the first line contains two integers, N (members to be tested) and M (relationships to be tested)(2 <= N, M <= 100). Then M lines follow, each contains a pair of (x, y) which means x is y’s master and y is x’s prentice. The input is terminated by N = 0.
TO MAKE IT SIMPLE, we give every one a number (0, 1, 2,…, N-1). We use their numbers instead of their names.
Output
For each test case, print in one line the judgement of the messy relationship.
If it is legal, output “YES”, otherwise “NO”.
Sample Input
3 2
0 1
1 2
2 2
0 1
1 0
0 0
Sample Output
YES
NO
题意:给出m组数据(x,y)表示x是y的主人,问给出的数据是否合法。

当没有入度为0的点时不合法。
用cnt计算入度为零时是否已经到达最后一个点。

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
#define M 510
int n,m;
int mp[M][M],du[M];
void topo()
{
	int i,j,count=0;
	queue <int> q;
	for(i=0;i<n;i++){
		if(du[i]==0)
			q.push(i);
	}
	while(!q.empty()){
		int tmp=q.front();
		q.pop(); 
		count++;//判断出队的点的个数 
		for(i=0;i<n;i++){
			if(mp[tmp][i]==1){
				if(--du[i]==0){
					q.push(i);
				}
			}
		}
	}
	if(count!=n) printf("NO\n");
	else printf("YES\n"); 
} 
int main()
{
	int i,a,b;
	while (scanf("%d%d",&n,&m),n+m){ 
		memset(mp,0,sizeof(mp));
		memset(du,0,sizeof(du));
		for(i=0;i<m;i++){
			scanf("%d%d",&a,&b);
			if(!mp[a][b]){
				mp[a][b]=1;
				du[b]++;
			}
		}
		topo();
	}
	return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值