0
点赞
收藏
分享

微信扫一扫

有向图判环+判连通性

有向图求拓扑序

  • 求拓扑序有\(dfs\)和\(bfs\)两种方法。
  • 在求拓扑序过程中,可以增加判断是否有环的代码。
  • 有些题目中,单独采用\(bfs\)或\(dfs\)只为了判断是否有环,而不是要输出拓扑序,这时代码可以适当简化。

有向图判环

例题:

​​\(HDU\) \(3342\) \(Legal\) \(or\) \(Not\)​​

1、\(bfs\)判环

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10, M = N << 1;
int ind[N];
int n, m;
//邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int q[N];
int main() {
while (~scanf("%d %d", &n, &m) && n) {
memset(ind, 0, sizeof(ind));
memset(h, -1, sizeof h);
idx = 0;

for (int i = 1; i <= m; i++) {
int a, b;
scanf("%d %d", &a, &b);
add(a, b);
ind[b]++; //记录入度
}
int hh = 0, tt = -1;
for (int i = 0; i < n; i++)
if (ind[i] == 0) q[++tt] = i; //入度为0的入队列

while (hh <= tt) {
int u = q[hh++];
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
ind[j]--; //拓扑模拟减去边,入度-1
if (ind[j] == 0) q[++tt] = j; //如果入度为0,则此节点入队列
}
}
//检查入过队列的节点个数是不是n个,相等则无环
hh == n ? puts("YES") : puts("NO");
}
return 0;
}

2、\(dfs\)判环

#include <bits/stdc++.h>
using namespace std;

const int N = 110, M = N << 1;

//邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int color[N]; // 染色法数组: 0:还没有访问过 1:正在访问中 2:它及它的子节点都已经访问过
int n, m; // n个顶点,m条边

bool dfs(int u) {
color[u] = 1;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (color[j] == 1) return true;
if (color[j] == 0 && dfs(j)) return true;
}
color[u] = 2;
return false;
}

int main() {
while (~scanf("%d%d", &n, &m) && n) {
memset(h, -1, sizeof(h)); //多组测试数据,每次输入清空地图
memset(color, 0, sizeof(color)); //多组测试数据,每次输入清空染色法数组
idx = 0;

for (int i = 1; i <= m; i++) { //有向图,m条边
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}

//是不是有环
bool flag = false;
// 因为本题的节点号是从0开始的
for (int i = 0; i < n; i++) { //枚举每个顶点,注意顶点号从0开始,至n-1止。
if (!color[i]) {
flag = dfs(i); //如果当前节点的颜色标识是0,表示还没有访问过,需要继续深搜,
if (flag) break;
}
}

!flag ? puts("YES") : puts("NO");
}
return 0;
}

3、强连通分量判环

#include <bits/stdc++.h>
using namespace std;
const int N = 1e2 + 10, M = N << 1;
//邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int low[N], dfn[N], stk[N];
int timestamp, top;
int scc;
bool in_stk[N];
void tarjan(int u) {
dfn[u] = low[u] = ++timestamp;
stk[++top] = u;
in_stk[u] = true;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (!dfn[j]) {
tarjan(j);
low[u] = min(low[u], low[j]);
} else if (in_stk[j])
low[u] = min(low[u], low[j]);
}
if (dfn[u] == low[u]) {
++scc;
int x;
do {
x = stk[top--];
in_stk[x] = false;
} while (x != u);
}
}

int main() {
int n, m;
while (~scanf("%d%d", &n, &m) && n) {
idx = 0;
memset(h, -1, sizeof(h));
while (m--) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}
//初始化
timestamp = scc = top = 0;
memset(in_stk, false, sizeof(in_stk));
memset(dfn, 0, sizeof(dfn));

for (int i = 0; i < n; i++)
if (!dfn[i]) tarjan(i);
//强连通分量数量等于节点数,说明无环,并且是一个连通图
scc == n ? puts("YES") : puts("NO");
}
return 0;
}

拓扑序的应用

​​https://www.jianshu.com/p/1cda977685b6​​




举报

相关推荐

0 条评论