这里写自定义目录标题
更多精彩内容
这里是带你游历编程世界的Dashcoding编程社,我是Dash/北航硕士/ICPC区域赛全国排名30+/给你呈现我们眼中的世界!
256题算法特训课,帮你斩获大厂60W年薪offer
原题
2024考研408真题-唯一拓扑排序
B站动画详解
问题分析
在有向图中,拓扑排序是对图中所有顶点的线性排序,使得对于每一条有向边 (u, v),顶点 u 在顶点 v 之前出现。拓扑排序仅适用于有向无环图(DAG)。拓扑排序的唯一性问题,即是否存在唯一的拓扑排序,实际上是在判断图中是否存在唯一的拓扑序列。在拓扑排序过程中,如果存在多个顶点的入度为0,那么就会存在多种可能的拓扑排序方式,因此我们需要检查图是否能够保证在任何情况下都只有一种合法的拓扑排序方式。
为了确定一个图是否有唯一的拓扑排序,我们需要使用拓扑排序算法,并在过程中检查是否在任何时刻存在多个入度为0的顶点。如果在执行拓扑排序时发现队列中的元素数目大于1,则说明存在多个入度为0的顶点,这会导致拓扑排序的多样性,从而图中存在多种拓扑排序。
思路分析
判断一个图是否存在唯一的拓扑排序,关键在于理解拓扑排序的唯一性条件。对于一个有向图,如果存在某个顶点有多个入度为0的顶点,则可以选择不同的顶点作为拓扑排序的起点,导致存在多种可能的拓扑排序。因此,为了确保拓扑排序的唯一性,在执行拓扑排序的过程中,每一步都应该只有一个顶点的入度为0。如果在任意一步中,有多个顶点的入度为0,那么该图就有多个拓扑排序,唯一性就不成立。
在这道题目中,我们采用基于广度优先搜索(BFS)的拓扑排序算法。在执行拓扑排序时,维护一个队列,每次从队列中取出一个入度为0的顶点,并将其相邻的顶点的入度减1。如果在某个时刻,队列中有超过一个入度为0的顶点,则意味着存在多种拓扑排序方案,此时返回0,表示拓扑排序不唯一;如果拓扑排序能够完整进行,并且在每一步中队列中始终只有一个入度为0的顶点,则返回1,表示拓扑排序唯一。
算法实现
我们采用了拓扑排序的基本思想,但在此基础上增加了判断唯一性的逻辑。具体来说,算法流程如下:
-
初始化:首先统计每个顶点的入度,并将入度为0的顶点加入队列。如果初始时刻队列中的元素不止一个,则直接返回0,因为此时存在多种选择,导致拓扑排序不唯一。
-
执行拓扑排序:进入循环,每次从队列中取出一个入度为0的顶点,并将其相邻顶点的入度减1。如果在此过程中,发现队列中有超过一个入度为0的顶点,则返回0,表示不唯一。
-
判断结束条件:如果在队列为空时,已经处理了所有顶点,则表明拓扑排序唯一,返回1;否则,返回0。
整个过程通过一个简单的BFS遍历实现,利用队列维护当前可以加入拓扑序列的顶点。
代码详解
标准代码程序
C++代码
#include "dashoj_2024_408.h"
int uniquely(MGraph G) {
struct queue {
int q[maxV * 2]; // 队列数组,存储顶点
int front = 1, rear = 0, sz = 0; // 初始化队列指针和大小
// 入队操作
void push(int v) {
q[++rear] = v;
sz++;
}
// 出队并返回队首元素
int getFrontAndPop() {
int v = q[front++];
sz--;
return v;
}
// 返回队列的大小
int size() {
return sz;
}
} Q;
int in[maxV]; // 记录每个顶点的入度
int cnt = 0; // 统计已经加入拓扑序列的顶点数量
// 初始化入度数组
for (int i = 1; i <= G.numberVertices; i++) in[i] = 0;
// 统计每个顶点的入度
for (int i = 1; i <= G.numberVertices; i++)
for (int j = 1; j <= G.numberVertices; j++) {
if (G.edge[i][j] == 1) in[j]++;
}
// 将入度为0的顶点入队
for (int i = 1; i <= G.numberVertices; i++) {
if (in[i] == 0) {
Q.push(i