《算法笔记》10.6小节——图算法专题->拓扑排序

10.6.1有向无环图

10.6.2拓扑排序

将有向无环图G的所有顶点排成一个线性序列,这个序列被称为拓扑序列。

习题

问题 A: 算法7-12:有向无环图的拓扑排序
问题描述:在本题中,读入一个有向图的邻接矩阵(即数组表示),建立有向图并按照以上描述中的算法判断此图是否有回路,如果没有回路则输出拓扑有序的顶点序列。

  • 输入
输入的第一行包含一个正整数n,表示图中共有n个顶点。其中n不超过50。
以后的n行中每行有n个用空格隔开的整数01,对于第i行的第j个整数,如果为1,则表示第i个顶点有指向第j个顶点的有向边,0表示没有i指向j的有向边。当i和j相等的时候,保证对应的整数为0
  • 输出
如果读入的有向图含有回路,请输出“ERROR”,不包括引号。
如果读入的有向图不含有回路,请按照题目描述中的算法依次输出图的拓扑有序序列,每个整数后输出一个空格。
请注意行尾输出换行。
  • 样例输入
4
0 1 0 0
0 0 1 0
0 0 0 0
0 0 1 0
  • 样例输出
3 0 1 2 

这题注意使用栈而不是队列保存度为0的点,然后栈先进后出,满足题目的输出要求

#define _CRT_SECURE_NO_WARNINGS 1
#include <cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
const int maxv = 60;//n不超过50
vector<int>G[maxv];
int n, inDegree[maxv];
vector<int>path;
bool topoSort()
{
	int num=0;
	stack<int> q;
	for (int i = 0; i < n; i++)
	{
		if (inDegree[i] == 0)
		{
			q.push(i);			
		}
			
	}
	while (!q.empty())
	{
		int u = q.top();
		q.pop();
		path.push_back(u);
		for (int i = 0; i < G[u].size(); i++)
		{
			int v = G[u][i];
			inDegree[v]--;
			if (inDegree[v] == 0)
			{
				q.push(v);
			}
		}
		G[u].clear();
		num++;
	}
	if (num == n) return true;
	else return false;
}
int main()
{	
	int temp;
	while (scanf("%d", &n) != EOF)
	{
		for (int i = 0; i < n; i++)
		{
			inDegree[i] = 0;
		}
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < n; j++)
			{
				scanf("%d", &temp);
				if (temp == 1)
				{
					G[i].push_back(j);//i->j
					inDegree[j]++;
				}					
			}
				
		}
		path.clear();
		
		if (topoSort())
		{
			for (int i = 0; i < path.size(); i++)
				printf("%d ",path[i]);
			printf("\n");
		}
		else
		{
			printf("ERROR\n");
		}
		
	}
	return 0;
}

问题 B: 确定比赛名次
问题描述:有N个比赛队(1<=N<=500),编号依次为1,2,3,。。。。,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。

  • 输入
输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示队伍的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即P1队赢了P2队。
  • 输出
给出一个符合要求的排名。输出时队伍号之间有空格,最后一名后面没有空格。
其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
  • 样例输入
3 2
3 1
3 2
17 16
16 1
13 2
7 3
12 4
12 5
17 6
10 7
11 8
11 9
16 10
13 11
15 12
15 13
17 14
17 15
17 16
0 0
  • 样例输出
3 1 2
17 6 14 15 12 4 5 13 2 11 8 9 16 1 10 7 3
#define _CRT_SECURE_NO_WARNINGS 1
#include <cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
const int maxv = 510;//n不超过50
vector<int>G[maxv];
int n, m,inDegree[maxv];
vector<int>path;
bool topoSort()
{
	int num=0;
	priority_queue<int,vector<int>,greater<int>> q;
	for (int i = 1; i <= n; i++)
	{
		if (inDegree[i] == 0)
		{
			q.push(i);			
		}			
	}
	while (!q.empty())
	{
		int u = q.top();
		q.pop();
		path.push_back(u);
		for (int i = 0; i < G[u].size(); i++)
		{
			int v = G[u][i];
			inDegree[v]--;
			if (inDegree[v] == 0)
			{
				q.push(v);
			}
		}
		G[u].clear();
		num++;
	}
	if (num == n) return true;
	else return false;
}
int main()
{	
	int temp1,temp2;
	while (scanf("%d %d", &n,&m) != EOF)
	{
		if (n == 0 && m == 0)
			break;
		else
		{
			for (int i = 1; i <= n; i++)
			{
				inDegree[i] = 0;
			}
			for (int i = 1; i <=m; i++)
			{
				scanf("%d%d", &temp1, &temp2);
				G[temp1].push_back(temp2);
				inDegree[temp2]++;											
			}			
			path.clear();
			topoSort();			
			for (int i = 0; i < path.size(); i++)
			{
				printf("%d", path[i]);
				if (i == path.size() - 1)
					printf("\n");
				else
					printf(" ");
			}					
		}		
	}
	return 0;
}

问题 C: Legal or Not
问题描述:ACM-DIY是一个大型QQ群,众多优秀的acmers齐聚一堂。太和谐了,就像一个大家庭。每天都有HH、hh、AC、ZT、lcc、BF、Qinz等众多“神牛”在线聊天,交流心得。当有人有疑问时,许多像迷路的热心奶牛会来帮忙。然后被帮助的人会称Lost为“主人”,Lost会有一个很好的“徒弟”。渐渐地,出现了许多对“师徒”。但是问题来了:师父太多,徒弟太多,怎么知道合法不合法呢?大家都知道一个师父可以有很多徒弟,一个徒弟也可以有很多师父,合法。然而,有些奶牛并不那么诚实,它们之间存在非法关系。以HH和3xian为例,HH是3xian的主人,同时,3xian是HH的主人,这很违法!为了避免这种情况,请帮助我们判断他们的关系是否合法。请注意,“主人和徒弟”关系是可传递的。这意味着如果 A 是 B 的主人,而 B 是 C 的主人,那么 A 是 C 的主人。

  • 输入
输入由几个测试用例组成。对于每种情况,第一行包含两个整数,N(要测试的成员)和 M(要测试的关系)(2 <= N,M <= 100)。然后是 M 行,每行包含一对 (x, y),这意味着 x 是 y 的主人,y 是 x 的徒弟。输入以 N = 0 结束。为了简单起见,我们给每个人一个数字 (0, 1, 2,..., N-1)。我们使用他们的号码而不是他们的名字。
  • 输出
对于每个测试用例,将乱序关系的判断打印在一行,如果合法,则输出“YES”,否则输出“NO”。
  • 样例输入
4 3
0 1
1 2
2 3
3 3
0 1
1 2
2 0
0 1
  • 样例输出
YES
NO

使用拓扑排序判断有向图的环,经典,记得要初始化邻接表G!

#define _CRT_SECURE_NO_WARNINGS 1
#include <cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
const int maxv = 110;//n不超过50
vector<int>G[maxv];
int n, m,inDegree[maxv];
bool topoSort()
{
	int num=0;
	queue<int> q;
	for (int i = 0; i < n; i++)
	{
		if (inDegree[i] == 0)
		{
			q.push(i);			
		}			
	}
	while (!q.empty())
	{
		int u = q.front();
		q.pop();		
		for (int i = 0; i < G[u].size(); i++)
		{
			int v = G[u][i];
			inDegree[v]--;
			if (inDegree[v] == 0)
			{
				q.push(v);
			}
		}
		G[u].clear();
		num++;
	}
	if (num == n) return true;
	else return false;
}
int main()
{	
	int temp1,temp2;
	while (scanf("%d %d", &n,&m) != EOF)
	{
		if (n == 0)
			break;
		else
		{
			memset(inDegree, 0, sizeof(inDegree));
			for (int i = 0; i < n; ++i)
			{
				G[i].clear();
			}
			
			for (int i = 1; i <=m; i++)
			{
				scanf("%d%d", &temp1, &temp2);
				G[temp1].push_back(temp2);
				inDegree[temp2]++;											
			}				
			if (topoSort())
				printf("YES\n");
			else
				printf("NO\n");						
		}		
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值