【洛谷B3644】【模板】拓扑排序 / 家谱树 解题报告

洛谷B3644 - 【模板】拓扑排序 / 家谱树

题目描述

有个人的家族很大,辈分关系很混乱,请你帮整理一下这种关系。给出每个人的后代的信息。输出一个序列,使得每个人的后辈都比那个人后列出。

输入格式

1 1 1 行一个整数 N N N 1 ≤ N ≤ 100 1 \le N \le 100 1N100),表示家族的人数。接下来 N N N 行,第 i i i 行描述第 i i i 个人的后代编号 a i , j a_{i,j} ai,j,表示 a i , j a_{i,j} ai,j i i i 的后代。每行最后是 0 0 0 表示描述完毕。

输出格式

输出一个序列,使得每个人的后辈都比那个人后列出。如果有多种不同的序列,输出任意一种即可。

样例 #1

样例输入 #1

5
0
4 5 1 0
1 0
5 3 0
3 0

样例输出 #1

2 4 5 3 1

这题非常非常简单,就是一个拓扑排序模板题。
题目直接告诉你每个人的后代,其实就是每个顶点的直接后继,然后求这个图的拓扑排序序列。
关于拓扑排序的详细讲解,请看我这篇博客:图的应用3-有向无环图、拓扑排序
我一共写了三种做法,全都 A C AC AC,足见其简单,而且这题数据范围也很友好,邻接矩阵可以放心食用。
首先是邻接矩阵实现,完整代码如下:

//AC(100pts)
#define _CRT_SECURE_NO_WARNINGS 1
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 114;
int n, indegree[N], print[N];//顶点数,各点入度,拓扑序列数组
bool A[N][N];//邻接矩阵存图

inline void Init() {
	cin >> n;
	for (int i = 0; i <= n; ++i) {//初始化
		indegree[i] = 0;
		for (int j = 0; j <= n; ++j) {
			A[i][j] = false;
		}
	}
	for (int i = 1, j; i <= n; ++i) {
		for (int k = 0; k <= n; ++k) {
			cin >> j;
			if (!j)break;//读到0结束当前顶点的后代读入
			A[i][j] = true;//邻接矩阵加入有向边
			indegree[j]++;//后代的入度加1
		}
	}
	return;
}

inline bool TopologicalSort() {//拓扑排序
	int Sta[N], top = 0, count = 0;//静态数组模拟栈
	memset(Sta, 0, sizeof(Sta));//初始化栈
	int i;
	for (i = 1; i <= n; ++i)
		if (!indegree[i])Sta[++top] = i;//入度为0的顶点入栈
	while (top) {
		i = Sta[top--];//栈顶元素出栈
		print[++count] = i;//输出i
		for (int j = 1; j <= n; ++j)
			if (A[i][j]) {//若i到j直接存在一条边
				A[i][j] = false, indegree[j]--;//删除该边,j的入度减1
				if (!indegree[j])Sta[++top] = j;//若j的入度减为0,则入栈
			}
	}
	if (count < n)return false;//若计数小于顶点数,说明存在环
	else return true;//否则拓扑排序成功
}

int main() {
	Init();
	if (!TopologicalSort())cout << "Error!" << endl;//排序失败
	else {//排序成功
		for (int i = 1; i <= n; ++i)
			cout << print[i] << " ";//输出拓扑排序序列
		cout << endl;
	}
	return 0;
}

邻接矩阵的完整代码也可看我的Github:传送门

↑ ↑ 内附赠逆拓扑排序实现代码)

然后是邻接表实现,之前我贴的讲解博客里有各函数的拆分讲解。
完整代码如下:

//AC(100pts)
#define _CRT_SECURE_NO_WARNINGS 1
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

#define MaxVertexNum 114
typedef struct ArcNode {
	int adjvex;
	struct ArcNode* nextarc;
}ArcNode, * ArcList;
typedef struct VNode {
	int data;
	ArcNode* firstarc;
}VNode, AdjList[MaxVertexNum];
typedef struct {
	AdjList vertices;
	int vexnum, arcnum;
}ALGraph;

int indegree[MaxVertexNum], print[MaxVertexNum];
//记录各点入度,记录拓扑排序序列

inline void Init(ALGraph& G) {//预处理
	for (int i = 1; i <= G.vexnum; ++i) {
		ArcList h = (ArcList)malloc(sizeof(ArcList));
		h->adjvex = 0;
		h->nextarc = NULL;
		G.vertices[i].data = 0;
		G.vertices[i].firstarc = h;
	}
	memset(indegree, 0, sizeof(indegree));
	memset(print, 0, sizeof(print));
	return;
}

inline void AG_Insert(ALGraph& G, int i, int j) {//头插法加入边
	ArcNode* p = (ArcNode*)malloc(sizeof(ArcNode));
	p->adjvex = j;
	ArcNode* head = G.vertices[i].firstarc;
	ArcNode* tail = G.vertices[i].firstarc->nextarc;
	p->nextarc = tail;
	head->nextarc = p;
	return;
}

inline bool Topologicalsort(ALGraph G) {
	int Sta[MaxVertexNum], top = 0, count = 0;
	//用静态数组模拟栈,top为栈顶指针,count为拓扑序列数组下表
	memset(Sta, 0, sizeof(Sta));
	int i;
	for (i = 1; i <= G.vexnum; ++i)
		if (!indegree[i])Sta[++top] = i;//将初始入度为0的顶点进栈
	while (top) {//当栈不空时
		i = Sta[top--];//栈顶元素出栈
		print[++count] = i;//输出顶点i
		for (ArcNode* p = G.vertices[i].firstarc; p != NULL; p = p->nextarc) {
			//将所有i指向的顶点入度减1,并将入度减为0的顶点压入栈Sta中
			int v = p->adjvex;
			if (!v)continue;
			if (!(--indegree[v]))
				Sta[++top] = v;//度为0则入栈
			p->adjvex = 0;//删除边
		}
	}
	if (count < G.vexnum)return false;//排序失败,图中存在回路
	else return true;//拓扑排序成功
}

int main() {
	ALGraph G;
	cin >> G.vexnum;
	Init(G);
	for (int i = 1, j; i <= G.vexnum; ++i) {
		for (int k = 0; k <= G.vexnum; ++k) {
			cin >> j;
			if (!j)break;
			AG_Insert(G, i, j);
			indegree[j]++;
		}
	}
	if (!Topologicalsort(G))cout << "No DAG!" << endl;
	else {
		for (int i = 1; i <= G.vexnum; ++i)
			cout << print[i] << " ";
		cout << endl;
	}
	return 0;
}

最后是邻接表的 D F S DFS DFS实现,预处理和基本操作函数之类的直接用上面的邻接表实现,原理详解请看之前我贴的讲解博客
完整代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

#define MaxVertexNum 114
typedef struct ArcNode {
	int adjvex;
	struct ArcNode* nextarc;
}ArcNode, * ArcList;
typedef struct VNode {
	int data;
	ArcNode* firstarc;
}VNode, AdjList[MaxVertexNum];
typedef struct {
	AdjList vertices;
	int vexnum, arcnum;
}ALGraph;

int indegree[MaxVertexNum], print[MaxVertexNum];
//记录各点入度,记录拓扑排序序列

inline void Init(ALGraph& G) {//预处理
	for (int i = 1; i <= G.vexnum; ++i) {
		ArcList h = (ArcList)malloc(sizeof(ArcList));
		h->adjvex = 0;
		h->nextarc = NULL;
		G.vertices[i].data = 0;
		G.vertices[i].firstarc = h;
	}
	memset(indegree, 0, sizeof(indegree));
	memset(print, 0, sizeof(print));
	return;
}

inline void AG_Insert(ALGraph& G, int i, int j) {//头插法加入边
	ArcNode* p = (ArcNode*)malloc(sizeof(ArcNode));
	p->adjvex = j;
	ArcNode* head = G.vertices[i].firstarc;
	ArcNode* tail = G.vertices[i].firstarc->nextarc;
	p->nextarc = tail;
	head->nextarc = p;
	return;
}

int tim, finishtime[MaxVertexNum];//计时变量,各顶点用时数组
bool visited[MaxVertexNum];//访问标记

inline void DFS(ALGraph G, int v) {
	visited[v] = true;
	for (ArcNode* p = G.vertices[v].firstarc->nextarc; p != NULL; p = p->nextarc) {
		int w = p->adjvex;//依次遍历当前顶点的邻边未访问的顶点
		if (!visited[w])DFS(G, w);
	}
	finishtime[v] = ++tim;//搜索深度越深,tim值越小
	//如果要输出逆拓扑排序序列,只需把这一行改为输出v即可
	return;
}

inline void DFSTravere(ALGraph G) {
	memset(visited, false, sizeof(visited));//初始化
	memset(finishtime, 0, sizeof(finishtime));
	tim = 0;
	for (int i = 1; i <= G.vexnum; ++i)//从第一个顶点开始深搜
		if (!visited[i])DFS(G, i);
	return;
}

int main() {
	ALGraph G;
	cin >> G.vexnum;
	Init(G);
	for (int i = 1, j; i <= G.vexnum; ++i) {
		for (int k = 0; k <= G.vexnum; ++k) {
			cin >> j;
			if (!j)break;
			AG_Insert(G, i, j);
			indegree[j]++;
		}
	}
	DFSTravere(G);//深搜
	int con = 0;//计数
	for (int i = 1; i <= G.vexnum; ++i) {
		int maxn = -1, maxm = 0;
		for (int j = 1; j <= G.vexnum; ++j)
			if (finishtime[j] > maxn)
				maxn = finishtime[j], maxm = j;
				//每次循环找到finishtime数组里tim值最大的顶点
		finishtime[maxm] = -1;//删除当前tim的最大值
		print[++con] = maxm;//输出当前tim值最大的顶点
	}
	for (int i = 1; i <= G.vexnum; ++i)
		cout << print[i] << " ";//输出拓扑排序序列
	cout << endl;
	return 0;
}

邻接表的两个实现完整代码也可以看我的Github:传送门

不过我没写判环操作,话说有环的话那就不是 D A G DAG DAG图了……


总之还是非常简单的一题,但是有点费时间了。
以上。

相关推荐

  1. 1191. 家谱拓扑排序模板题)

    2024-07-16 01:06:03       58 阅读
  2. 】【模板排序

    2024-07-16 01:06:03       63 阅读
  3. ——P1347 排序(图论-拓扑排序)

    2024-07-16 01:06:03       56 阅读
  4. B3622

    2024-07-16 01:06:03       46 阅读
  5. 刷题 | B3623 枚举排列

    2024-07-16 01:06:03       36 阅读
  6. 模板拓扑排序

    2024-07-16 01:06:03       58 阅读
  7. 帮贡排序#

    2024-07-16 01:06:03       44 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-16 01:06:03       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-16 01:06:03       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-16 01:06:03       58 阅读
  4. Python语言-面向对象

    2024-07-16 01:06:03       69 阅读

热门阅读

  1. typora图片问题以及快捷键问题汇总

    2024-07-16 01:06:03       21 阅读
  2. [Selenium]C#语言中的等待策略的应用与实现

    2024-07-16 01:06:03       18 阅读
  3. 刷题——有效括号序列

    2024-07-16 01:06:03       24 阅读
  4. Ningx配置前端http缓存

    2024-07-16 01:06:03       23 阅读