tarjan算法c语言,tarjan算法板子 - osc_e45irv7l的个人空间 - OSCHINA - 中文开源技术交流社区...

无向图

概念

时间戳

\(dfn[x]\),在深度优先遍历中,按照每个节点第一次被访问的顺序,依次做整数标记

追溯值

\(low[x]\),通过非搜索边能到达的最小时间戳

割边判定法则

无向边\((x,y)\)是割边/桥,当且仅当存在x的一个子节点满足\(dfn[x] < low[y]\)

删除无向边\((x,y)\)后,图断开成两个部分

板子

int dfn[N], low[N], dfcnt;

bool g[M];

void tarjan(int x, int ei) {

dfn[x] = low[x] = ++dfcnt;

for(int i = head[x]; i; i = e[i].next) {

int y = e[i].t;

if (!dfn[y]) {

tarjan(y, i);

low[x] = min(low[x], low[y]);

if (dfn[x] < low[y]) g[i] = g[i^1] = 1;

}

else if (i != (ei^1)) low[x] = min(low[x], dfn[y]);

}

}

割点判定法则

若x不是根节点,则x是割点当且仅当存在一个子节点y满足\(dfn[x]\leq low[y]\)

若x是根节点,则x是割点当且仅当存在至少两个子节点\(y_1,y_2\)满足上条件

板子

int dfn[N], low[N], dfcnt, rt;

bool g[N];

void tarjan(int x) {

dfn[x] = low[x] = ++dfcnt;

int son = 0;

for (int i = head[x]; i; i = e[i].next) {

int y = e[i].t;

if (!dfn[y]) {

tarjan(y);

low[x] = min(low[x], low[y]);

if (dfn[x] <= low[y]) {

son++;

if (x != rt || son > 1) g[x] = 1;

}

}

else low[x] = min(low[x], dfn[y]);

}

}

点双联通分量

对于,每个点双中来说,图里是不存在割点的

板子

int dfn[N], low[N], dfcnt, sta[N], top, cnt;

vector dcc[N];

bool g[N];

void tarjan(int x, int rt) {

dfn[x] = low[x] = ++dfcnt;

sta[++top] = x;

int son = 0;

for (int i = head[x]; i; i = e[i].next) {

int y = e[i].t;

if (!dfn[y]) {

tarjan(y);

low[x] = min(low[x], low[y]);

if (dfn[x] <= low[y]) {

son++;

if (x != rt || son > 1) g[x] = 1;

dcc[++cnt].clear();

while (1) {

int z = sta[top--];

dcc[cnt].push_back(z);

if (y == z) break;

}

dcc[cnt].push_back(x);

}

}

else low[x] = min(low[x], dfn[y]);

}

}

边双联通分量

对于一个边双,任意两个点都有两条不重合的路径

板子

int dfn[N], low[N], dfcnt;

bool g[M];

void tarjan(int x, int ei) {

dfn[x] = low[x] = ++dfcnt;

for(int i = head[x]; i; i = e[i].next) {

int y = e[i].t;

if (!dfn[y]) {

tarjan(y, i);

low[x] = min(low[x], low[y]);

if (dfn[x] < low[y]) g[i] = g[i^1] = 1;

}

else if (i != (ei^1)) low[x] = min(low[x], dfn[y]);

}

}

int n, m, d[N], b[N], cnt, ans;

void dfs(int x) {

b[x] = cnt;

for(int i = head[x]; i; i = e[i].next) {

int y = e[i].t;

if (b[y] || g[i]) continue;

dfs(y);

}

}

int main() {

//~~~

for(int i = 1; i <= n; i++)

if (!dfn[i]) tarjan(i, 0);

for(int i = 1; i <= n; i++)

if (!b[i]) cnt++, dfs(i);

//~~~

return 0;

}

有向图

有向图的强联通分量

在一个强联通分量中,存在x到y的路径,就存在y到x的路径

板子

void tarjan(int x) {

dfn[x] = low[x] = ++dfcnt;

s[++top] = x;

for(int i = head[x]; i; i = e[i].next) {

int y = e[i].t;

if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);

else if (!b[y]) low[x] = min(low[x], dfn[y]);

}

if (dfn[x] == low[x]) {

cnt++;

while(1) {

int y = s[top--];

b[y] = cnt;

size[cnt]++;

if (x == y) break;

}

}

}

例题

luoguP3387缩点

代码

#include

#include

#include

#include

using namespace std;

const int N = 1e4+5, M = 1e5+5;

struct side { int t, next; } e[M][2];

int head[N][2], tot[2];

void add(int x, int y, int k) {

e[++tot[k]][k].next = head[x][k];

head[x][k] = tot[k];

e[tot[k]][k].t = y;

}

int n, m, w[N], r[N], d[N], ans;

int dfn[N], low[N], dfcnt, sta[N], top, cnt, bel[N], sum[N];

void tarjan(int x) {

dfn[x] = low[x] = ++dfcnt;

sta[++top] = x;

for (int i = head[x][0]; i; i = e[i][0].next) {

int y = e[i][0].t;

if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);

else if (!bel[y]) low[x] = min(low[x], dfn[y]);

}

if (dfn[x] == low[x]) {

cnt++;

while (1) {

int y = sta[top--];

bel[y] = cnt;

sum[cnt] += w[y];

if (x == y) break;

}

}

}

queue q;

int tuopu() {

for (int i = 1; i <= cnt; i++)

if (!r[i]) q.push(i), d[i] = sum[i];

while (!q.empty()) {

int x = q.front(); q.pop();

for (int i = head[x][1]; i; i = e[i][1].next) {

int y = e[i][1].t;

d[y] = max(d[y], d[x] + sum[y]);

if (--r[y] == 0) q.push(y);

}

}

for (int i = 1; i <= cnt; i++)

ans = max(ans, d[i]);

return ans;

}

int main() {

scanf("%d%d", &n, &m);

for (int i = 1; i <= n; i++)

scanf("%d", &w[i]);

for (int i = 1; i <= m; i++) {

int x, y;

scanf("%d%d", &x, &y);

add(x, y, 0);

}

for (int i = 1; i <= n; i++)

if (!dfn[i]) tarjan(i);

for (int x = 1; x <= n; x++)

for (int i = head[x][0]; i; i = e[i][0].next) {

int y = e[i][0].t;

if (bel[x] != bel[y])

r[bel[y]]++, add(bel[x], bel[y], 1);

}

printf("%d\n", tuopu());

return 0;

}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部