Tarjan

有向图的强连通分量:

因为是复习,所以在此不进行算法赘述

算法介绍:https://www.cnblogs.com/mhpp/p/6751723.html

#include<iostream>
#include<vector>
#include<stack>
using namespace std;

vector<int> g[1001];
stack<int> s;
int n,m,scc[1001],x,y,low[1001],dfn[1001],ind,sccNum[1001],sccV;

void tarjan(int u){
    low[u]=dfn[u]=++ind;
    s.push(u);
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }else{
            if(!scc[v]){
                low[u]=min(low[u],dfn[v]);
            }
        }
    }
    if(low[u]==dfn[u]){
        sccV++;
        int v;
        do{
            v=s.top();
            s.pop();
            scc[v]=sccV;
            sccNum[sccV]++;
        }while(u!=v);
    }
}

int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>x>>y;
        g[x].push_back(y);
    }
    for(int i=1;i<=n;i++){
        if(dfn[i]==0)tarjan(i);
    }
    for(int i=1;i<=n;i++){
        if(sccNum[scc[i]]>1)cout<<"T"<<endl;
        else cout<<"F"<<endl;
    }
}

时间复杂度:

O(n+m) n为点数,m为边数

割点与桥:

定义:
  • 割点:无向连通图中,去掉一个顶点及和它相邻的所有边,图中的连通分量数增加,则该顶点称为割点

  • 桥(割边):无向连通图中,去掉一条边,图中的连通分量数增加,则这条边,称为桥或者割边

割点与桥(割边)的关系:

1)有割点不一定有桥,有桥一定存在割点

2)桥一定是割点依附的边。

下图中顶点C为割点,但和C相连的边都不是桥

判定方法:

ps: v为u的子节点

  1. 如果low[v] >= dfn[u]:
    就说明 V 访问 U 的祖先顶点,必须通过 U ,所以 U 就是一个割点

  2. 如果low[v] > dfn[u]:
    就说明 V-U 是桥(类似上文)

双连通图:

边双连通图:一个图无桥

构造方法:

首先按桥缩点,形成的图一定是一棵树,有桥。

然后统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf。

则至少在树上添加(leaf+1)/2条边,就能使树达到双连通,所以至少添加的边数就是(leaf+1)/2。

具体方法:

在两个最近公共祖先最远的叶节点之间连接一条边,这样这两个点到祖先的路径加这条边就形成了一个环。

以此类推,恰好需要执行(leaf+1)/2次操作。
//按桥缩点 (leaf+1)/2= 添最少的边能使无向图变成双连通图
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
struct edge{
    int t,id;
};
vector<edge> g[5001];
stack<int> s;
int low[5001],dfn[5001],bcc[5001],deg[5001],bcc_num,n,m,x,y,ind,br,leaf;
bool net[5001][5001];
void tarjan(int u,int fa) {
    dfn[u]=low[u]=++ind;
    s.push(u);
    for(int i=0; i<g[u].size(); i++) {
        int v=g[u][i].t;
        if(!dfn[v]) {
            tarjan(v,g[u][i].id);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u]) {
                bcc_num++;
                br++;
                int k;
                do {
                    k=s.top();
                    s.pop();
                    bcc[k]=bcc_num;
                } while(k!=v);
            }
        } else {
            if(dfn[v]<dfn[u]&&fa/2!=g[u][i].id/2) {
                low[u]=min(low[u],dfn[v]);
            }
        }
    }
    if(fa==-1) {
        bcc_num++;
        while(!s.empty()) {
            int k=s.top();
            s.pop();
            bcc[k]=bcc_num;
        }
    }
}

int main() {
    cin>>n>>m;
    for(int i=0; i<m; i++) {
        cin>>x>>y;
        g[x].push_back((edge){y,i*2});
        g[y].push_back((edge){x,i*2+1});
    }
    for(int i=1; i<=n; i++) {
        if(!dfn[i])tarjan(i,-1);
    }
    for(int i=1; i<=n; i++) {
        for(int j=0; j<g[i].size(); j++) {
            int k=g[i][j].t;
            if(bcc[i]!=bcc[k]&&net[bcc[i]][bcc[k]]==false) {
                net[bcc[i]][bcc[k]]=true;
                net[bcc[k]][bcc[i]]=true;
                deg[bcc[i]]++;
                deg[bcc[k]]++;
            }
        }
    }
    for(int i=1; i<=bcc_num; i++) {
        if(deg[i]==1)leaf++;
    }
    cout<<br<<endl;
    cout<<(leaf+1)/2<<endl;
}

点双连通图:一个图无割点

//求割点及每个割点把图分成几部分 
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
vector<int> g[1001];
int dfn[1001],low[1001],cp[1001],n,s,t,ind,c=1;
bool flag;
void init(){
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(cp,0,sizeof(cp));
    for(int i=1;i<=1000;i++)g[i].clear();
    ind=0;
    flag=false;
    c++;
    n=0;
}

void tarjan(int u,int fa){
    dfn[u]=low[u]=++ind;
    int child=0;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(!dfn[v]){
            child++;
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])cp[u]++;
        }else{
            if(dfn[v]<dfn[u]&&v!=fa){
                low[u]=min(low[u],dfn[v]);
            }
        }
    }
    if(fa<0&&child==1)cp[u]=0;
}

void work(){
    if(flag==false)return;
    for(int i=1;i<=n;i++){
        if(!dfn[i])tarjan(i,-1);
    }
    cout<<"Network #"<<c<<endl;
    bool spf=false;
    for(int i=1;i<=n;i++){
        if(cp[i]!=0){
            if(i==1)cp[i]--;
            cout<<"  SPF node "<<i<<" leaves "<<cp[i]+1<<" subnets"<<endl;
            spf=true;
        }
    }
    if(spf==false){
        cout<<"  No SPF nodes"<<endl;
    }
    cout<<endl;
}

int main(){
    while(cin>>s){
        if(s==0){
            work();
            init();
            continue;   
        }
        cin>>t;
        flag=true;
        g[s].push_back(t);
        g[t].push_back(s);  
        n=max(n,s);
        n=max(n,t); 
    }
}
//找点双——栈里存点 
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int N=5e4+10;
const int M=3e5+10;
struct Edge{int v,nxt;}edge[M<<1];
int cnt,head[N],n,m;
int dfn[N],low[N],ind,s[N],top;
vector<int> dcc[N];int num;
void addedge(int u,int v){
	edge[++cnt].v=v;edge[cnt].nxt=head[u];head[u]=cnt;
}
void tarjan(int u,int fa){
	dfn[u]=low[u]=++ind;
	s[++top]=u;
	if(fa==-1&&!head[u]){//孤立点
        dcc[++num].push_back(u);
        return;
    }
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].v;
		if(!dfn[v]){
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]){
				num++;
				int x=0;
				do{
					x=s[top--];
					dcc[num].push_back(x); 
				}while(x!=v);
				dcc[num].push_back(u);//u加到点双里,但不出栈 
			}
		}
		else if(dfn[v]<dfn[u]&&v!=fa){
			low[u]=min(low[u],dfn[v]);
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v;scanf("%d%d",&u,&v);
		addedge(u,v);
		addedge(v,u);
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i]) tarjan(i,-1);
	for(int i=1;i<=num;i++){
		for(int j=0;j<dcc[i].size();j++) printf("%d ",dcc[i][j]);
		puts("");
	}
	return 0;
} 
//找点双——栈里存边
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N=1010;
const int M=100010;
struct Edge{
	int u,v,nxt;
}edge[M<<2];
int n,m,cnt,head[N];
int ind,low[N],dfn[N],num,bn[N];
int s[M],top;
int no[N][N],odd[N],col[N];
vector<int> dcc[N];
void init(){
	memset(bn,0,sizeof(bn));
    memset(no,0,sizeof(no));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(odd,0,sizeof(odd));
    memset(head,0,sizeof(head));
    cnt=0,ind=0,num=0,top=0;
}
void addedge(int u,int v){
	edge[++cnt].u=u;edge[cnt].v=v;edge[cnt].nxt=head[u];head[u]=cnt;
}
void tarjan(int u,int fa){
    low[u]=dfn[u]=++ind;
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].v;
        if(!dfn[v]){
            s[++top]=i;//先压栈再遍历
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]){
                num++;
                dcc[num].clear();
                int x;
                do{
                	x=s[top--];
                	if(bn[edge[x].u]!=num){
                		dcc[num].push_back(edge[x].u);
						bn[edge[x].u]=num;
					}
                    if(bn[edge[x].v]!=num){
                    	dcc[num].push_back(edge[x].v);
						bn[edge[x].v]=num;
					}
				}while(x!=i);
            }
        }
        else if(dfn[v]<dfn[u]&&v!=fa){
            s[++top]=i; 
            low[u]=min(low[u],dfn[v]);
        }
    }
}
int dfs(int u,int k){
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].v;
        if(bn[v]!=k) continue;  
        if(col[v]==-1){  
            col[v]=col[u]^1;  
            if(!dfs(v,k)) return 0;
        }
        else if(col[v]==col[u]) return 0;
    }
    return 1;
}
int main(){
    while(1){
        init();
        scanf("%d%d",&n,&m);
        if(!n&&!m) break;
        for(int i=1;i<=m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            if(!x&&!y) break;
            no[x][y]=1;no[y][x]=1;
        }
        for(int i=1;i<=n;i++){
        	for(int j=1;j<=n;j++){
                if(i==j||no[i][j]) continue;
                addedge(i,j);
            }
		}
        for(int i=1;i<=n;i++)
            if(!dfn[i]) tarjan(i,0);
        for(int i=1;i<=num;i++){
            for(int j=0;j<dcc[i].size();j++)
                col[dcc[i][j]]=-1,bn[dcc[i][j]]=i;
            col[dcc[i][0]]=1;
            if(!dfs(dcc[i][0],i)){
                for(int j=0;j<dcc[i].size();j++) odd[dcc[i][j]]=1;
            }
        }
        int ans=n;
        for(int i=1;i<=n;i++)
            if(odd[i]) ans--;
        cout<<ans<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值