关于
if (dfn[i]): # 已经被处理过
low[now] = min(low[now], dfn[i])
为什么是dfn[i]而不是low[i]:Tarjan-深度剖析
tarjan裸代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200007;
const int MAXM = 1000007;
int n, m, x, y, cnt, indexx;
int head[MAXN], dfn[MAXN], low[MAXN];
stack<int> sta;//放入强连通分量
map<int, bool> vis;//判断该点是否在栈内
struct EDGE {
int u;
int v;
int next;
} edge[MAXM];
void Add(int u, int v) {
edge[++cnt].u = u;
edge[cnt].v = v;
edge[cnt].next = head[u];
head[u] = cnt;
}
void tarjan(int u) {
dfn[u] = low[u] = ++indexx;
sta.push(u);
vis[u] = true;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if (!dfn[v]) {//子节点未被找过
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (vis[v])//子节点已被找过且仍在栈中
low[u] = min(low[u], dfn[v]);
}
int cur = -1;
if (dfn[u] == low[u]) {
do {
cur = sta.top();
sta.pop();
vis[cur] = false;
printf("%d ", cur);
} while (u != cur);
printf("\n");
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
cin >> x >> y;
Add(x, y);
}
for (int i = 1; i <= n; ++i)
if (!dfn[i])
tarjan(i);
return 0;
}
//6 8 1 4 1 2 2 5 2 3 3 6 4 5 5 6 5 1
缩点:poj2186 Popular Cows
被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 A 喜欢 B,B 喜欢 C,那么 A 也喜欢 C。牛栏里共有 N 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200007;
const int MAXM = 1000007;
int n, m, x, y, cnt, indexx;
int head[MAXN], dfn[MAXN], low[MAXN];
stack<int> sta;
map<int, bool> vis;
int du[MAXN], id[MAXN], all[MAXN], tot;//此为添加内容
//tot为强连通分量数量,id为强连通分量编号,all为每个强连通分量中包含的节点,du为出度
struct EDGE {
int u;
int v;
int next;
} edge[MAXM];
void Add(int u, int v) {
edge[++cnt].u = u;
edge[cnt].v = v;
edge[cnt].next = head[u];
head[u] = cnt;
}
void tarjan(int u) {
dfn[u] = low[u] = ++indexx;
sta.push(u);
vis[u] = true;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if (!dfn[v]) {//子节点未被找过
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (vis[v])//子节点已被找过且仍在栈中
low[u] = min(low[u], dfn[v]);
}
int cur = -1;
if (dfn[u] == low[u]) {此为更改内容
++tot;
do {
cur = sta.top();
sta.pop();
vis[cur] = false;
id[cur] = tot;
all[tot]++;
} while (u != cur);
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
cin >> x >> y;
Add(x, y);
}
for (int i = 1; i <= n; ++i)
if (!dfn[i])
tarjan(i);
for (int u = 1; u <= n; ++u) {
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if (id[u] != id[v]) {
du[id[u]]++;//记录出度
}
}
}
int flag = 0;
for (int i = 1; i <= tot; ++i) {
if (!du[i]) {
if (flag) {//出现了第二个出度为0的点
cout << 0 << endl;
return 0;
}
flag = i;
}
}
printf("%d\n", all[flag]);
return 0;
}
/*
3 3
1 2
2 1
2 3
*/
割点:洛谷P3388 【模板】割点(割顶)
给出一个 n 个点,m 条边的无向图,求图的割点。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200007;
const int MAXM = 1000007;
int n, m, x, y, cnt, indexx;
int head[MAXN], dfn[MAXN], low[MAXN];
bool cut[MAXN];
struct EDGE {
int u;
int v;
int next;
} edge[MAXM];
void Add(int u, int v) {
edge[++cnt].u = u;
edge[cnt].v = v;
edge[cnt].next = head[u];
head[u] = cnt;
}
void tarjan(int u, int fa) {
dfn[u] = low[u] = ++indexx;
int child = 0;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if (!dfn[v]) {
tarjan(v, fa);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u] && u != fa) {
cut[u] = true;
}
if (u == fa)
child++;
} else
low[u] = min(low[u], dfn[v]);
}
if (child >= 2 && u == fa) {
cut[u] = true;
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
cin >> x >> y;
Add(x, y);
Add(y, x);
}
for (int i = 1; i <= n; ++i)
if (!dfn[i])
tarjan(i, i);
int ans = 0;
for (int i = 1; i <= n; ++i)
if (cut[i])
ans++;
cout << ans << endl;
for (int i = 1; i <= n; ++i) {
if (cut[i])
cout << i << " ";
}
return 0;
}
/*
6 7
1 2
1 3
1 4
2 5
3 5
4 5
5 6
*/