强连通分量 Tarjan模板 , 割点模板,割边(桥)模板 , 缩点

1.输出各强连通分量

      浅析强连通分量(Tarjan和kosaraju)Tarjan模板 

      例题(求强连通分量的个数)

#include <cstdio>
#include <stack>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=10005;
const int maxm=100005;
int n,m,idx=0,k=1,Bcnt=0;
int head[maxn];
int ins[maxn]={0};
int dfn[maxn]={0},low[maxn]={0};
//int Belong[maxn];
stack <int> s;
struct edge
{
    int v,next;
}e[maxm];
int min(int a,int b)
{
    return a<b?a:b;
}
void adde(int u,int v)
{
     e[k].v=v;
     e[k].next=head[u];
     head[u]=k++;
}

void tarjan(int u)
{
     int v;
     dfn[u]=low[u]=++idx;//每次dfs,u的次序号增加1
     s.push(u);//将u入栈
     ins[u]=1;//标记u在栈内
     for(int i=head[u];i!=-1;i=e[i].next)//访问从u出发的边
     {
         v=e[i].v;
         if(!dfn[v])//如果v没被处理过
         {
             tarjan(v);//dfs(v)
             low[u]=min(low[u],low[v]);//u点能到达的最小次序号是它自己能到达点的最小次序号和连接点v能到达点的最小次序号中较小的
         }
         else if(ins[v])low[u]=min(low[u],dfn[v]);//如果v在栈内,u点能到达的最小次序号是它自己能到达点的最小次序号和v的次序号中较小的
     }     
     if(dfn[u]==low[u])
     {
         Bcnt++;
         do
         {
             v=s.top();
             s.pop();
             ins[v]=0;
        //     Belong[v]=Bcnt;   //把属于第Bcnt个强连通分量的节点v的Belong都为Bcnt,如果节点3,4属于第2个强连通分量,则使 Belong[3],Belong[4]都等于2;
         }while(u != v);
     }
}
void work()
{
     for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
//     printf("\n");                                
//     for(int i = 1;i <= 6;i++)printf("%d %d\n",dfn[i],low[i]);
//     printf("共有%d强连通分量,它们是:\n",Bcnt); 
   if(Bcnt==1){
   		printf("Yes\n");
   }else{
   		printf("No\n");
   }
  //   for(int i=1;i<=Bcnt;i++)
//     {
//        printf("第%d个:",i);
//        for(int j=1;j<=n;j++)
//        {
//           if(Belong[j]==i)printf("%d ",j);
//        }
//        printf("\n");
//     }
}
int main()
{
     int a,b;
     while(~scanf("%d%d",&n,&m)){
     	idx=0,k=1,Bcnt=0;
     	memset(ins,0,sizeof(ins));
     	memset(dfn,0,sizeof(dfn));
     	memset(low,0,sizeof(low));
     	memset(head,-1,sizeof(head));
     	if(n==0&&m==0){
     		return 0;
     	}
     for(int i=1;i<=m;i++)
     {
         scanf("%d%d",&a,&b);
         adde(a,b);
     }
     work();
	 }
}

2.割点模板

      例题(求割点数目)

      如果想输出各个割点,只需通过迭代器访问set即可。

# include<iostream>
# include<cstdio>
# include<set>
# include<vector> 
# include<cstring>
# define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=1000;
int low[maxn],dfn[maxn],root,now,m;
vector<int>g[maxn];
set<int>ans;   //ans用来盛装割点
void init(){
	now=0;
	mem(dfn,0);
	mem(low,0);
	ans.clear();
    for( int i = 0; i < maxn; i++ ) g[i].clear();
} 
void addedge(int u,int v){
	g[u].push_back(v);
	g[v].push_back(u);
}
void tarjan(int u,int fa){
	low[u]=dfn[u]=++now;
	int len=g[u].size();
	int son=0;
	for(int i=0;i<len;++i){
		int v=g[u][i];
		if(v==fa)continue;
		if(!dfn[v]){
			tarjan(v,u);
			son++;
			low[u]=min(low[u],low[v]);
			if(u==root&&son>1){
				ans.insert(u);
			}else if(u!=root&&dfn[u]<=low[v]){
				ans.insert(u);
			}
		}else{
			low[u]=min(low[u],dfn[v]);
		}
	}
}
int main(){
	while(scanf("%d",&m)&&m!=0)
    {
        init();
        int n;
        while(scanf("%d",&n)&&n!=0)
        {
            while(getchar()!='\n')
            {
                int a;
                scanf("%d",&a);
                addedge(n,a);
            }
        }
        root=1;
        
        tarjan(1,-1);
       
        printf("%d\n",ans.size());
    }
    return 0;
}

3.割边(桥)模板

例题

# include<iostream>
# include<cstdio>
# include<set>
# include<vector> 
# include<cstring>
# include<algorithm>
# define mem(a,b) memset(a,b,sizeof(a))
# define p pair<int ,int>
using namespace std;
const int maxn=1e5+5;
int low[maxn],dfn[maxn],now,ecnt;
vector<int>g[maxn];
//set<p>ans;   //pair型的ans同时容纳一条边的两个端点 (不知为啥会wa)
struct Ans{
	int u,v;
}ans[maxn];    //ans容纳一条边的两个端点 
void init(){
	ecnt=0;
	now=0;
	mem(dfn,0);
	mem(low,0);
	mem(ans,0);
    for( int i = 0; i < maxn; i++ ) g[i].clear();
} 
int cmp(Ans a,Ans b){
	if(a.u>b.u) return a.u<b.u;
	else if(a.u==b.u && a.v>b.v) return a.v<b.v;
	return 1;
}
void addedge(int u,int v){
	g[u].push_back(v);
	g[v].push_back(u);
}
void tarjan(int u,int fa){
	low[u]=dfn[u]=++now;
	int len=g[u].size();

	for(int i=0;i<len;++i){
		int v=g[u][i];
		if(v==fa)continue;
		if(!dfn[v]){
			tarjan(v,u);
		
			low[u]=min(low[u],low[v]);
			if (dfn[u] < low[v]){
                //ans.insert(make_pair(min(u,v), max(u,v)));
                int a,b;
                a=u,b=v;
                if(a>b) swap(a,b);
                ans[ecnt].u=a,ans[ecnt].v=b;
                ecnt++;
            }
		}else{
			low[u]=min(low[u],dfn[v]);
		}
	}
}
int main(){
	int n, x, y, m;
    while (scanf("%d", &n) != EOF) {
        init();
        for (int i = 0; i < n; ++i) g[i].clear();
        for (int i = 0; i < n; ++i) {
            scanf("%d (%d)", &x, &m);
            for (int j = 0; j < m; ++j) {
                scanf("%d", &y);
                addedge(x, y);
            }
        }
        for (int i = 0; i < n; ++i) {
            if (dfn[i]) continue;
            tarjan(i, i);
        }
        //int sum = ans.size();
//        printf("%d critical links\n", sum);
//        set<p>::iterator it=ans.begin();  //迭代器访问ans 
//        for (;it!=ans.end();it++) {
//            printf("%d - %d\n", *it);  //一个*it包含pair中的两个int (wa)
//        }
		sort(ans,ans+ecnt,cmp);
		cout<<ecnt<<" critical links"<<endl;
		for(int i=0;i<ecnt;i++){
			printf("%d - %d\n",ans[i].u,ans[i].v);
		}
        printf("\n");
    }
    return 0;
}

4.缩点

题目

#include <cstdio>
#include <stack>
#include <cstring>
#include <iostream>
using namespace std;

const int maxm=50050;
int n,m,idx=0,k=1,Bcnt=0;
int head[maxm];
int ins[maxm]={0};
int dfn[maxm]={0},low[maxm]={0};
int Belong[maxm];
int outdegree[maxm]; 
stack <int> s;
struct edge
{
    int v,next;
}e[maxm];
int min(int a,int b)
{
    return a<b?a:b;
}
void adde(int u,int v)
{
     e[k].v=v;
     e[k].next=head[u];
     head[u]=k++;
}

void tarjan(int u)            //仍是不变的模板
{
     int v;
     dfn[u]=low[u]=++idx;//每次dfs,u的次序号增加1
     s.push(u);//将u入栈
     ins[u]=1;//标记u在栈内
     for(int i=head[u];i!=-1;i=e[i].next)//访问从u出发的边
     {
         v=e[i].v;
         if(!dfn[v])//如果v没被处理过
         {
             tarjan(v);//dfs(v)
             low[u]=min(low[u],low[v]);//u点能到达的最小次序号是它自己能到达点的最小次序号和连接点v能到达点的最小次序号中较小的
         }
         else if(ins[v])low[u]=min(low[u],dfn[v]);//如果v在栈内,u点能到达的最小次序号是它自己能到达点的最小次序号和v的次序号中较小的
     }     
     if(dfn[u]==low[u])
     {
         Bcnt++;
         do
         {
             v=s.top();
             s.pop();
             ins[v]=0;
             Belong[v]=Bcnt;   //把属于第Bcnt个强连通分量的节点v的Belong都为Bcnt,如果节点3,4属于第2个强连通分量,则使 Belong[3],Belong[4]都等于2; 
         }while(u != v);
     }
}
void work()
{
     for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
//     printf("\n");                                
//     for(int i = 1;i <= 6;i++)printf("%d %d\n",dfn[i],low[i]);
//     printf("共有%d强连通分量,它们是:\n",Bcnt); 
 //  if(Bcnt==1){
 //  		printf("Yes\n");
 //  }else{
 //  		printf("No\n");
 //  }
  //   for(int i=1;i<=Bcnt;i++)
//     {
//        printf("第%d个:",i);
//        for(int j=1;j<=n;j++)
//        {
//           if(Belong[j]==i)printf("%d ",j);
//        }
//        printf("\n");
//     }
		for(int i=1;i<=n;i++){            //求每个强连通分量的出度(如果某个强连通分量包含多个点,将他们缩为一个超级点)
			for(int j=head[i];j!=-1;j=e[j].next){
				int v=e[j].v;
				if(Belong[i]!=Belong[v])
					outdegree[Belong[i]]++;
			}
		}
		int sum=0,temp,ans=0;
		for(int i=1; i<=Bcnt; i++)
 	    {
 	       if(!outdegree[i])              //出度为零,并且这样的点只有一个才输出1,否则输出0
 	           sum++,temp=i;
	    }
		if(sum==1)
		{
  	      for(int i=1; i<=n; i++)
  	          if(Belong[i]==temp)
  	              ans++;
  	    }
  	    else
  	      ans=0;
  	    printf("%d\n",ans);
}
int main()
{
     int a,b;
     while(~scanf("%d%d",&n,&m)){
     	idx=0,k=1,Bcnt=0;
     	memset(ins,0,sizeof(ins));
     	memset(dfn,0,sizeof(dfn));
     	memset(low,0,sizeof(low));
     	memset(head,-1,sizeof(head));
    
     for(int i=1;i<=m;i++)
     {
         scanf("%d%d",&a,&b);
         adde(a,b);
     }
     work();
	 }
}

阿里云建站—为企业提供互联网“快”服务

2020年因为一场突如其来的疫情,不少企业受到了严重冲击,疫情的冲击力对传统“纯线

下”行业的危害最大,互联网女皇玛丽·米克(MaryMeeker)4月17日发布了著名的年度互

联网趋势报告,报告中指出:拥有强大的互联网线上线下融合能力的企业在疫情中的表现最好,

线上线下融合的趋势已经存在一段时间了,但是疫情让这种需求变得更加的迫切。

如果你的企业完全依附于传统的、纯线下的经验模式,那么在2020年你将“必死无疑”,

一场巨大的,前所未有的互联网革命已经到来!

阿里云建站为了助力各行各业复工复产,疫情期间“马不停蹄”为数以万计的企业快速完成

建站,为他们朝着“线上线下融合”或者“纯线上”的互联网经营模式迈进,打下了坚实的基础。

“云·速成美站”模板建站1天就能上线,不懂技术没关系,打字就能建网站。千套网站模

板免费提供,百元就能建官网,一价全包,无任何隐形消费。

“云·企业官网”定制建站1周就能上线,高端量身定制建站,千元建官网无需自己动手,

建站专家1对1网站策划及设计,专业省心之选。

疫情,是一场大浪淘沙,每一次危机背后都隐藏着机会,危机越大,机会也越大。大环境

已经改变,如果你不努力不去改变来迎合这个大环境,那你必将被淘汰。

阿里云助力企业建站,优惠多多,福利多多,详情请点击如下链接

红包+折扣,阿里云上云大礼包!https://www.aliyun.com/minisite/goods?userCode=

猜你喜欢

转载自blog.csdn.net/weixin_44075041/article/details/98585906