强联通分量 缩点 tarjan 入门题小集
来源:互联网 发布:海量数据排序 编辑:程序博客网 时间:2024/06/05 10:08
参考
强联通分量及缩点tarjan算法解析 ——九野的博客
强连通tarjan模版 ——九野的博客
HDU 1269
题意
判断给定的有向图是否强联通,即判断图中的强联通分量数是否为
Code
#include <bits/stdc++.h>#include <stack>#define maxn 100010using namespace std;struct Edge { int to, ne; Edge(int a = 0, int b = 0) : to(a), ne(b) {}}edge[maxn * 2];int low[maxn], dfn[maxn], cnt, tot, ne[maxn], n, m, scc;bool vis[maxn], in[maxn];stack<int> s;void add(int u, int v) { edge[tot] = Edge(v, ne[u]); ne[u] = tot++;}void dfs(int u) { vis[u] = true; low[u] = dfn[u] = cnt++; in[u] = true; s.push(u); for (int i = ne[u]; i != -1; i = edge[i].ne) { Edge e = edge[i]; int v = e.to; if (!vis[v]) { dfs(v); low[u] = min(low[u], low[v]); } else if (in[v]) low[u] = min(low[u], dfn[v]); } if (dfn[u] == low[u]) { ++scc; if (scc == 2) return; while (true) { int x = s.top(); s.pop(); in[x] = false; if (x == u) break; } }}void work() { memset(vis, 0, sizeof(vis)); memset(ne, -1, sizeof(ne)); memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); cnt = tot = scc = 0; for (int i = 0; i < m; ++i) { int u, v; scanf("%d%d", &u, &v); add(u, v); } bool flag = false; for (int i = 1; i <= n; ++i) { if (!vis[i]) dfs(i); if (scc > 1) break; } if (scc == 1) printf("Yes\n"); else printf("No\n");}int main() { while (scanf("%d%d", &n, &m) != EOF && n + m) work(); return 0;}
HDU 1827
题意
给定一张有向图,一个节点为一个人,边
思路
先缩点,得到一个
另:POJ 2186与之类似,只要找新图中出度为
Code
#include <bits/stdc++.h>#define inf 0x3f3f3f3f#include <stack>#include <vector>#define maxn 1010using namespace std;typedef long long LL;stack<int> s;vector<int> bcc[maxn];struct Edge { int from, to, ne; Edge(int a=0, int b =0, int c = 0) : from(a), to(b), ne(c) {}}edge[maxn * 2];int dfn[maxn], low[maxn], ne[maxn], belong[maxn], deg[maxn], tot, cnt, scc, val[maxn];bool in[maxn];void add(int u, int v) { edge[tot] = Edge(u, v, ne[u]); ne[u] = tot++;}void init() { cnt = tot = scc = 0; while (!s.empty()) s.pop(); memset(ne, -1, sizeof(ne)); memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); memset(belong, 0, sizeof(belong)); memset(deg, 0, sizeof(deg)); memset(in, 0, sizeof(in));}void tarjan(int u) { dfn[u] = low[u] = ++cnt; in[u] = true; s.push(u); for (int i = ne[u]; i != -1; i = edge[i].ne) { int v = edge[i].to; if (!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if (in[v]) low[u] = min(low[u], dfn[v]); } if (low[u] == dfn[u]) { ++scc; bcc[scc].clear(); while (true) { int v = s.top(); in[v] = false; s.pop(); belong[v] = scc; bcc[scc].push_back(v); if (v == u) break; } }}void contract() { for (int i = 0; i < tot; ++i) { int u = edge[i].from, v = edge[i].to; if (belong[u] == belong[v]) continue; ++deg[belong[v]]; }}int n, m;void work() { init(); for (int i = 1; i <= n; ++i) scanf("%d", &val[i]); for (int i = 0; i < m; ++i) { int u, v; scanf("%d%d", &u, &v); add(u, v); } for (int i = 1; i <= n; ++i) { if (!dfn[i]) tarjan(i); } contract(); LL ans = 0; int anc = 0; for (int i = 1; i <= scc; ++i) { if (deg[i] == 0) { ++anc; int minn = inf; for (auto x : bcc[i]) minn = min(minn, val[x]); ans += minn; } } printf("%d %lld\n", anc, ans);}int main() { while (scanf("%d%d", &n, &m) != EOF) work(); return 0;}
HDU 3836
同 hdu2767.
题意
在有向图中加最少的有向边使得图成为一个强联通分量。
思路
先缩点。要使新图成为一个强联通分量,形象一点想,即是让所有的首尾相接,于是统计“首”和“尾”的个数,即入度为
Code
#include <bits/stdc++.h>#include <stack>#define maxn 20010#define maxm 50010using namespace std;stack<int> s;int dfn[maxn], low[maxn], ne[maxn], in[maxn], belong[maxn], ind[maxn], outd[maxn], cnt, tot, scc;struct Edge { int from, to, ne; Edge(int a = 0, int b = 0, int c = 0) : from(a), to(b), ne(c) {}}edge[maxm];void add(int u, int v) { edge[tot] = Edge(u, v, ne[u]); ne[u] = tot++;}void init() { cnt = tot = scc = 0; memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); memset(ne, -1, sizeof(ne)); memset(in, 0, sizeof(in)); memset(ind, 0, sizeof(ind)); memset(outd, 0, sizeof(outd)); while (!s.empty()) s.pop();}void tarjan(int u) { dfn[u] = low[u] = ++cnt; in[u] = true; s.push(u); for (int i = ne[u]; i != -1; i = edge[i].ne) { int v = edge[i].to; if (!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if (in[v]) low[u] = min(low[u], dfn[v]); } if (low[u] == dfn[u]) { ++scc; while (true) { int v = s.top(); in[v] = false; s.pop(); belong[v] = scc; if (u == v) break; } }}void contract() { for (int i = 0; i < tot; ++i) { int u = edge[i].from, v = edge[i].to; if (belong[u] == belong[v]) continue; ++outd[belong[u]], ++ind[belong[v]]; }}int n, m;void work() { init(); while (m--) { int u, v; scanf("%d%d", &u, &v); add(u, v); } for (int i = 1; i <= n; ++i) { if (!dfn[i]) tarjan(i); } if (scc == 1) { printf("0\n"); return; } contract(); int intot = 0, outtot = 0; for (int i = 1; i <= scc; ++i) { if (!ind[i]) ++intot; if (!outd[i]) ++outtot; } printf("%d\n", max(intot, outtot));}int main() { while (scanf("%d%d", &n, &m) != EOF) work(); return 0;}
吐槽
这道题写得实在是太不走心了…wa了好几发 ++outd[belong[u]], ++ind[belong[v]];
写成了++outd[u], ++ind[v];
下面枚举的时候明明应该是新图的点数
真是可怕啊
HDU 4635
题意
给定一个有向图,问至多加多少条边之后,图仍然不强联通。
分析
(这道题很有意思,和上一道题可以说是恰好反过来~)
正难则反。
如果真要考虑去加边的话,要考虑:1. 在每个已形成的强联通分量内加边;2. 在缩点形成后的图中加边,其中还要考虑入/出度为 反正我是不会做。
考虑先将给定的图加边加成完全图,再从完全图中删除尽量少的一些刚刚加入的边使得图不强联通。
1. 加边很容易,当前图边数为
2. 删边的话,一旦将某一个点删成入/出度为
在缩点后形成的
最后答案即为
Code
#include <bits/stdc++.h>#define maxn 100010#include <stack>#define inf 0x3f3f3f3fusing namespace std;stack<int> s;int dfn[maxn], low[maxn], ne[maxn], sz[maxn], belong[maxn], outd[maxn], ind[maxn], tot, cnt, scc, in[maxn], kas;struct Edge { int from, to, ne; Edge(int a = 0, int b = 0, int c = 0) : from(a), to(b), ne(c) {}}edge[maxn];void add(int u, int v) { edge[tot] = Edge(u, v, ne[u]); ne[u] = tot++;}void init() { tot = cnt = scc = 0; while (!s.empty()) s.pop(); memset(ne, -1, sizeof(ne)); memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); memset(outd, 0, sizeof(outd)); memset(ind, 0, sizeof(ind)); memset(in, 0, sizeof(in));}void tarjan(int u) { dfn[u] = low[u] = ++cnt; in[u] = true; s.push(u); for (int i = ne[u]; i != -1; i = edge[i].ne) { int v = edge[i].to; if (!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if (in[v]) low[u] = min(low[u], dfn[v]); } if (low[u] == dfn[u]) { ++scc; sz[scc] = 0; while (true) { int v = s.top(); in[v] = false; s.pop(); ++sz[scc]; belong[v] = scc; if (v == u) break; } }}void contract() { for (int i = 0; i < tot; ++i) { int u = edge[i].from, v = edge[i].to; if (belong[u] == belong[v]) continue; ++outd[belong[u]], ++ind[belong[v]]; }}void work() { int n, m; init(); scanf("%d%d", &n, &m); for (int i = 0; i < m; ++i) { int u, v; scanf("%d%d", &u, &v); add(u, v); } for (int i = 1; i <= n; ++i) { if (!dfn[i]) tarjan(i); } if (scc == 1) { printf("Case %d: -1\n", ++kas); return; } contract(); int minn = inf; for (int i = 1; i <= scc; ++i) { if (!outd[i] || !ind[i]) minn = min(minn, sz[i]); } printf("Case %d: %lld\n", ++kas, 1LL * n * (n - 1) - m - 1LL * minn * (n - minn));}int main() { int T; scanf("%d", &T); while (T--) work(); return 0;}
ZOJ 3630
题意
给定一张有向图,要求删除其中一个点,使得剩下的图中最大的强联通分量的
题目保证一个点至多在一个强联通分量中。
思路
记原图中最大的强联通分量为
所删之点必然是
最后的答案必然是
枚举原图最大的强联通分量中的点即可。
Code
#include <bits/stdc++.h>#define maxn 10000#include <stack>#include <vector>#define inf 0x3f3f3f3fusing namespace std;stack<int> s;vector<int> bcc[maxn];int dfn[maxn], low[maxn], ne[maxn], sz[maxn], tot, cnt, scc, in[maxn];bool exist[maxn];struct Edge { int from, to, ne; Edge(int a = 0, int b = 0, int c = 0) : from(a), to(b), ne(c) {}}edge[maxn];void add(int u, int v) { edge[tot] = Edge(u, v, ne[u]); ne[u] = tot++;}void init() { tot = 0; memset(ne, -1, sizeof(ne));}void tarjanInit() { cnt = scc = 0; while (!s.empty()) s.pop(); memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); memset(in, 0, sizeof(in));}void tarjan(int u) { dfn[u] = low[u] = ++cnt; in[u] = true; s.push(u); for (int i = ne[u]; i != -1; i = edge[i].ne) { int v = edge[i].to; if (!exist[v]) continue; if (!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if (in[v]) low[u] = min(low[u], dfn[v]); } if (low[u] == dfn[u]) { ++scc; sz[scc] = 0; bcc[scc].clear(); while (true) { int v = s.top(); in[v] = false; s.pop(); ++sz[scc]; bcc[scc].push_back(v); if (v == u) break; } }}int n, m;void work() { init(); tarjanInit(); for (int i = 0; i < m; ++i) { int u, v; scanf("%d%d", &u, &v); ++u, ++v; add(u, v); } for (int i = 1; i <= n; ++i) exist[i] = true; for (int i = 1; i <= n; ++i) { if (!dfn[i]) tarjan(i); } int ans, idx, idx2; if (scc == 1) ans = 0, idx = 1; else { if (sz[1] >= sz[2]) idx = 1, idx2 = 2; else idx = 2, idx2 = 1; for (int i = 3; i <= scc; ++i) { if (sz[i] >= sz[idx]) { idx2 = idx; idx = i; } else if (sz[i] > sz[idx2]) idx2 = i; } ans = sz[idx2]; } if (sz[idx] == 1) { printf("0\n"); return; } vector<int> V = bcc[idx]; int siz = sz[idx]; memset(exist, 0, sizeof(exist)); for (int i = 0; i != V.size(); ++i) exist[V[i]] = true; int minn = inf; for (int i = 0; i != siz; ++i) { tarjanInit(); exist[V[i]] = false; for (int i = 0; i != V.size(); ++i) { if (exist[V[i]] && !dfn[V[i]]) tarjan(V[i]); } int maxx = 0; for (int j = 1; j <= scc; ++j) { maxx = max(maxx, sz[j]); } minn = min(minn, maxx); exist[V[i]] = true; } ans = max(minn, ans); if (ans == 1) ans = 0; printf("%d\n", ans);}int main() { while (scanf("%d%d", &n, &m) != EOF) work(); return 0;}
HDU 6165&POJ 2186
麻烦移步本菜另一篇文章_(:з」∠)_
2017多校九 05题 hdu 6165 FFF at Valentine 缩点 dp找最长链/拓扑排序
- 强联通分量 缩点 tarjan 入门题小集
- 强联通分量 缩点 tarjan算法
- BZOJ 2438 杀人游戏 强联通分量tarjan缩点
- 强联通分量-tarjan
- 强联通分量+缩点
- Tarjan算法---强联通分量
- hdu1269 tarjan强联通分量
- 强联通分量 Tarjan算法
- tarjan求强联通分量
- tarjan求强联通分量
- “tarjan陪伴强联通分量
- HDOJ 1827 - Summer Holiday 简单的tarjan求强联通分量+缩点
- POJ 2186 Popular Cows(强联通分量缩点+tarjan算法)
- poj2186 强联通分量 缩点
- POJ2186 Tarjan强连通分量+缩点
- Tarjan求强连通分量 缩点
- tarjan强连通分量缩点笔记
- 迷宫城堡+tarjan+强联通分量基础题
- select poll epoll总结
- Linux软链接和硬链接
- LeetCode Binary Tree Tilt
- 2.2 数据类型2: Numeric & String
- mac Alfred+Dash快速查询API文档
- 强联通分量 缩点 tarjan 入门题小集
- python中的匿名函数lambda
- java动态代理
- TCP/IP部分指令命令解析
- Java常用API----日期操作
- Python检测手机号出现次数最多的数字
- 堆排序算法
- IO/OutputStream
- C++