10.6.1有向无环图
10.6.2拓扑排序
将有向无环图G的所有顶点排成一个线性序列,这个序列被称为拓扑序列。
习题
问题 A: 算法7-12:有向无环图的拓扑排序
问题描述:在本题中,读入一个有向图的邻接矩阵(即数组表示),建立有向图并按照以上描述中的算法判断此图是否有回路,如果没有回路则输出拓扑有序的顶点序列。
- 输入
输入的第一行包含一个正整数n,表示图中共有n个顶点。其中n不超过50。
以后的n行中每行有n个用空格隔开的整数0或1,对于第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;
}