Tarjan(强连通分量割点缩点)

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Tarjan(强连通分量割点缩点)
Tarjan(强连通分量割点缩点)
Tarjan 算法
所需的变量
变量名
dfn[maxn]当前节点是第⼏个被访问到的
low[maxn]当前节点所能访问到的最⼩的dfn
sta[maxn]存储可能构成强连通分量的栈
col[maxn]记录各个节点所属于的强连通分量编号
强连通分量
图中找到⼀个最⼤的图,使这个图中每个两点都能够互相到达。

这个最⼤的图称为强连通分量,同时⼀个点也属于强连通分量。

缩点
把所有环按照染⾊情况缩成⼀个点,重新连边
⾸先链式前向星建原图,来⼀波tarjan缩点,然后再链式前向星建⼀个新图
最后来个topo求从⼊度为零的点到它能⾛到的点的最⼤值
缩点code
const int maxn = 2e5 + 10;
int n,m;
int a[maxn];
int dfn[maxn],head[maxn],cnt,col[maxn],sta[maxn],top,tot;
int dep,f[maxn],p,ans[maxn],in[maxn],low[maxn],vis[maxn];
std::vector<int> ed[maxn];
struct node{
int v,next,u;
}e[maxn<<1];
void add(int u,int v) {
e[++cnt].next = head[u];
e[cnt].v = v;
e[cnt].u = u;
head[u] = cnt;
}
void tarjan(int u) {
dfn[u] = low[u] = ++dep;
vis[u] = 1;
sta[++top] = u;
for(int i = head[u]; i ;i = e[i].next) {
int v = e[i].v;
if(!dfn[v]) {
tarjan(v);
low[u] = min(low[u],low[v]);
}
else {
if(vis[v])
low[u] = min(low[u],low[v]);
}
}
if(dfn[u] == low[u]) {
col[u] = u;
vis[u] = 0;
while(sta[top] != u) {
col[sta[top]] = u;
a[u] += a[sta[top]];
vis[sta[top--]] = 0;
}
top--;
}
}
int topo() {
queue<int> q;
for(int i = 1; i <= n; i++) {
if(!in[i] && col[i] == i) {
q.push(i);
f[i] = a[i];
}
}
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i = 0; i < ed[u].size(); i++) {
int v = ed[u][i];
f[v] = max(f[v],f[u]+a[v]);
in[v]--;
if(in[v] == 0) {
q.push(v);
}
}
}
int sum = 0;
for(int i = 1; i <= n; i++) {
sum = max(sum,f[i]);
}
return sum;
}
int main() {
cin >> n >> m;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
for(int j = 1; j <= m; j++) {
int u,v;
cin >> u >> v;
add(u,v);
}
for(int i = 1; i <= n; i++) {
if(!dfn[i]) {
tarjan(i);
}
}
for(int i = 1; i <= m; i++) {
int x = col[e[i].u];
int y = col[e[i].v];
if(x != y) {
in[y]++;
ed[x].PB(y);
}
}
cout << topo() << "\n";
return 0;
}
割点和割桥
对于⽆向图有双连通分量
对于⼀个⽆向图,如果把⼀个点删除后这个图的极⼤连通分量数增加了,那么这个点就是这个图的割点(⼜称割顶)。

对于⼀个⽆向图,如果删掉⼀条边后图中的连通分量数增加了,则称这条边为桥或者割边
使⽤Tarjan判断是否为割点
对于祖先,如果它有两个以上⼉⼦,那么它必为割点
对于某个顶点u,如果存在v(u的⼉⼦) 使得low[v]>=dfn[u] 那么u为割点
割点code
const int maxn = 2e5 + 10;
int n,m;
struct node{
int v,next;
}e[maxn];
int head[maxn],cnt;
int dfn[maxn],low[maxn],ans[maxn];
int dep = 0,tot = 0;
void tarjan(int u,int fa) {
low[u] = dfn[u] = ++dep;
int an = 0;
for(int i = head[u]; i; i = e[i].next) {
int v = e[i].v;
if(!dfn[v]) {
tarjan(v,fa);
low[u] = min(low[u],low[v]);
if(u != fa && dfn[u] <= low[v]) {
ans[u] = 1;
}
if(u == fa) {
an++;
}
}
low[u] = min(low[u],dfn[v]); }
if(an >= 2 && u == fa) {
ans[u] = 1;
}
}
void add(int u,int v) {
e[++cnt].next = head[u]; e[cnt].v = v;
head[u] = cnt;
}
int main() {
cin >> n >> m;
for(int i = 1; i <= m; i++) { int u,v;
cin >> u >> v;
add(u,v);
add(v,u);
}
for(int i = 1; i <= n; i++) { if(dfn[i] == 0) {
tarjan(i,i);
}
}
for(int i = 1; i <= n; i++) { if(ans[i]) {
tot++;
}
}
cout << tot << "\n";
for(int i = 1; i <= n; i++) { if(ans[i]) {
cout << i << " ";
}
}
cout << "\n";
return 0;
}Processing math: 100%。

相关文档
最新文档