缩点[tarjan]
模板题:P3387 【模板】缩点
引语:
缩点是将强连通分量缩为一个点的算法,适用于有环无边权的图。(强连通分量定义:对于一个图的子图,若其中任何点间都存在路径可到达其他任何点,则该子图为一个强连通分量)
重难点数组含义:\(dfn\) 与 \(low\) 两个数组,\(dfn\) 维护 \(dfs\) 序,\(low_{u}\) 维护 \(u\) 或着说是 \(u\) 的子树能够追溯到的最早栈中节点的 \(dfs\) 序,栈维护当前搜索的一条链上的一个个点。
\(tarjan\) 核心 \(dfs\) 代码
void tarjan(int x) // 缩点(tarjan)
{
low[x] = dfn[x] = ++timeStamp; //初始化 low 与 dfn
stack[++top] = x; instack[x] = 1;
for (int i = head[x]; i; i = G[i].nxt)
{
int v = G[i].v;
if (!dfn[v])
{
tarjan(v);
low[x] = min(low[x], low[v]); //维护 low
}
else if (instack[v])
low[x] = min(low[x], low[v]);
}
if (dfn[x] == low[x]) // x 为强连通分量 dfs 出发点,开始进行缩点
{
int y;
while (y = stack[top--])
{
nodeId[y] = x;
instack[y] = 0;
if (x == y) break;
nodew[x] += nodew[y];
}
}
}
若 \(dfn_x = low_x\),则 \(x\) 为该强连通分量的 \(dfs\) 出发点。此时,栈中 \(x\) 至栈顶,均属于同一强连通分量。
模拟:
-
若一个结点 \(x\) 出度为 \(0\),则\(dfn_x = low_x\),自身为一个强连通分量。
-

\(dfs\) 过程:1->2->3->4->5->1,这样 \(1\)~\(5\) 的点的 \(low\) 的值均为 \(1\),回溯至 \(1\) 号节点时,\(dfn_1 = low_1\),开始进行缩点。
也可以查看这两篇博客的模拟过程:\(CSDN_1\) 与 \(CSDN_2\)
代码
#include <iostream>
#include <cstdio>
#include <queue>
#define MAXN 10005
#define MAXM 100005
using namespace std;
int n, m;
struct edge
{
int u, v, nxt;
}G[MAXM], G_[MAXM];
int head[MAXN], cntEdge, head_[MAXN], cntEdge_;
int dfn[MAXN], low[MAXN], timeStamp; // 记录时间戳
int stack[MAXN], top, instack[MAXN]; // 记录搜索链
int nodew[MAXN], nodeId[MAXN]; //点权,及点对应的强连通分量 dfs 出发点(有点并查集的感觉)
int ind[MAXN], dis[MAXN]; // 缩点后点的入度
int read()
{
int x = 0; char ch = getchar();
while (ch < '0' || ch > '9')
ch = getchar();
while (ch >= '0' && ch <= '9')
{
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return x;
}
inline void addEdge(edge *G, int *head, int &cntEdge, int u, int v)
{
++cntEdge;
G[cntEdge].u = u;
G[cntEdge].v = v;
G[cntEdge].nxt = head[u];
head[u] = cntEdge;
}
void init()
{
n = read(), m = read();
for (int i = 1; i <= n; ++i)
nodew[i] = read();
for (int i = 1; i <= m; ++i)
{
int u = read(), v = read();
addEdge(G, head, cntEdge, u, v);
}
}
void tarjan(int x) // 缩点(tarjan)
{
low[x] = dfn[x] = ++timeStamp;
stack[++top] = x; instack[x] = 1;
for (int i = head[x]; i; i = G[i].nxt)
{
int v = G[i].v;
if (!dfn[v])
{
tarjan(v);
low[x] = min(low[x], low[v]);
}
else if (instack[v])
low[x] = min(low[x], low[v]);
}
if (dfn[x] == low[x]) // x 为强连通分量 dfs 出发点
{
int y;
while (y = stack[top--])
{
nodeId[y] = x;
instack[y] = 0;
if (x == y) break;
nodew[x] += nodew[y];
}
}
}
int topo()
{
queue <int> q;
for (int i = 1; i <= n; ++i)
if (nodeId[i] == i && !ind[i])
{
q.push(i);
dis[i] = nodew[i];
}
while (!q.empty())
{
int u = q.front(); q.pop();
for (int i = head_[u]; i; i = G_[i].nxt)
{
int v = G_[i].v;
dis[v] = max(dis[v], dis[u] + nodew[v]);
--ind[v];
if (!ind[v])
q.push(v);
}
}
int ret = 0;
for (int i = 1; i <= n; ++i)
ret = max(ret, dis[i]);
return ret;
}
void solve()
{
for (int i = 1; i <= n; ++i)
if (!dfn[i])
tarjan(i);
for (int i = 1; i <= m; ++i)
{
int u = nodeId[G[i].u], v = nodeId[G[i].v];
if (u != v)
{
addEdge(G_, head_, cntEdge_, u, v);
++ind[v];
}
}
printf("%d\n", topo());
}
int main()
{
init();
solve();
return 0;
}

浙公网安备 33010602011771号