缩点|强连通分量
Posted cj-gjh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了缩点|强连通分量相关的知识,希望对你有一定的参考价值。
缩点|强连通分量一般解释自行百度,反正比较简单,一般性用在有向图中,可结合DP乱搞做题.
缩点
顾名思义,就是把一些点缩成几个点.
强连通分量
在一个有向图中,一个环就是一个强连通分量.
求法
我们不妨设dfn[i]表示到达第i个点的时间,low[i]表示第i个点可以到达的祖先中dfn的最小值.
这里我们引入几个概念(一个图中白点表示没有访问过的点,灰点表示访问了但是没有回溯到的点,黑点表示已经回溯了的点):
- 到白点的边为树边(T)
- 到灰点的边为返祖边(后向边)
- 到黑点的边为前向边
那么现在我们就可以做了
code
void Tarjan(int u){//当前访问到了u
dfn[u]=low[u]=++time;//时间戳
sta[++num]=u;ins[u]=1;//标记为灰点并进栈
for(int i=front[u];i;i=e[i].nxt){//链式前向星
int v=e[li].to;
if(!dfn[v]){//是个白点
Tarjan(v);
low[u]=min(low[u],low[v]);
//low[u]为u和v能够到达的最小的祖先的编号
}
else if(ins[v])low[u]=min(low[u],dfn[v]);
//这句话十分的神奇,因为有可能v的low值是有一条前向边.
}
//以上便完成了对low值的更新
if(dfn[u]==low[u]){//能够回到的最早祖先就是自己,说明是个强连通分量
int j;t++;//t为强连通分量的个数
do{
j=sta[num--];belong[j]=t;ins[j]=0;
//出栈操作并将j归为第t个强连通分量
}while(j!=u);
}
}
以上便是代码
例题
lg2835
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
#define ll long long
#define in inline
#define re register
in int gi(){
int sum=0,f=1;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
const int maxn=210,maxm=40010;
struct node{
int to,nxt;
}e[maxm];
int front[maxn],cnt,chu[maxn],inp[maxn];
void Add(int u,int v){
e[++cnt]=(node){v,front[u]};front[u]=cnt;
}
int dfn[maxn],low[maxn],tim;
int sta[maxn<<1],num,color[maxn],t;
void Trajan(int u){
dfn[u]=low[u]=++tim;sta[++num]=u;
inp[u]=1;
for(int i=front[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v]){
Trajan(v);
low[u]=min(low[u],low[v]);
}
else if(inp[v])low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
int j;t++;
do{
j=sta[num--];inp[j]=0;
color[j]=t;
}while(j!=u);
}
}
int main(){
int i,j,k,n,m;
n=gi();
for(i=1;i<=n;i++){
m=gi();
while(m){
Add(i,m);
m=gi();
}
}
for(i=1;i<=n;i++)
if(!dfn[i])Trajan(i);
for(i=1;i<=n;i++)
for(j=front[i];j;j=e[j].nxt){
int v=e[j].to;
if(color[i]!=color[v])chu[color[v]]=1;
}
int ans=0;
for(i=1;i<=t;i++)
if(!chu[i])ans++;
printf("%d\n",ans);
return 0;
}
lg3916
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
#define ll long long
#define in inline
#define re register
in int gi(){
int sum=0,f=1;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
const int maxn=100010,maxm=100010;
struct node{
int to,nxt;
}e[maxm];
int front[maxn],cnt,ins[maxn],num,t;
void Add(int u,int v){
e[++cnt]=(node){v,front[u]};front[u]=cnt;
}
int low[maxn],dfn[maxn],tim,ma[maxn],color[maxn],sta[maxm],A[maxn];
void dfs(int u,int d){
if(A[u])return;
A[u]=d;
for(int i=front[u];i;i=e[i].nxt){
int v=e[i].to;
dfs(v,d);
}
}
int main(){
int i,j,k,n,m;
n=gi(),m=gi();
for(i=1;i<=m;i++){
int u=gi(),v=gi();
Add(v,u);
}
for(i=n;i>=1;i--)dfs(i,i);
for(i=1;i<=n;i++)
printf("%d%c",A[i],i==n?'\n':' ');
return 0;
}
//这道题目和Trajan没有很大的关系...
lg2863
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
#define ll long long
#define in inline
#define re register
in int gi(){
int sum=0,f=1;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
const int maxn=10010,maxm=50010;
struct node{
int to,nxt;
}e[maxm];
int front[maxn],cnt,dfn[maxn],low[maxn],color[maxn],t,tim,siz[maxn],sta[maxn],ins[maxn],num;
void Add(int u,int v){
e[++cnt]=(node){v,front[u]};front[u]=cnt;
}
void Trajan(int u){
dfn[u]=low[u]=++tim;
sta[++num]=u;ins[u]=1;
for(int i=front[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v]){Trajan(v);low[u]=min(low[u],low[v]);}
else if(ins[v])low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
int j;t++;
do{
j=sta[num--];ins[j]=0;
color[j]=t;siz[t]++;
}while(j!=u);
}
}
int main(){
int i,j,k,n,m;
n=gi();m=gi();
for(i=1;i<=m;i++){
int u=gi(),v=gi();
Add(u,v);
}
for(i=1;i<=n;i++)
if(!dfn[i])Trajan(i);
int ans=0;
for(i=1;i<=t;i++)
if(siz[i]>1)ans++;
printf("%d\n",ans);
return 0;
}
lg2002
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
#define ll long long
#define in inline
#define re register
in int gi(){
int sum=0,f=1;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
const int maxn=100010,maxm=500010;
struct node{
int to,nxt;
}e[maxm];
int front[maxn],cnt,chu[maxn],inp[maxn];
void Add(int u,int v){
e[++cnt]=(node){v,front[u]};front[u]=cnt;
}
int dfn[maxn],low[maxn],tim;
int sta[maxn<<1],num,color[maxn],t;
void Trajan(int u){
dfn[u]=low[u]=++tim;sta[++num]=u;
inp[u]=1;
for(int i=front[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v]){
Trajan(v);
low[u]=min(low[u],low[v]);
}
else if(inp[v])low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
int j;t++;
do{
j=sta[num--];inp[j]=0;
color[j]=t;
}while(j!=u);
}
}
int main(){
int i,j,k,n,m;
n=gi();m=gi();
for(i=1;i<=m;i++){
int u=gi(),v=gi();
Add(u,v);
}
for(i=1;i<=n;i++)
if(!dfn[i])Trajan(i);
for(i=1;i<=n;i++)
for(j=front[i];j;j=e[j].nxt){
int v=e[j].to;
if(color[i]!=color[v])chu[color[v]]=1;
}
int ans=0;
for(i=1;i<=t;i++)
if(!chu[i])ans++;
printf("%d\n",ans);
return 0;
}
以上是关于缩点|强连通分量的主要内容,如果未能解决你的问题,请参考以下文章
tarjan算法(强连通分量 + 强连通分量缩点 + 桥 + 割点 + LCA)