图论:割点、缩点、桥

tarjan

缩点

点击查看代码块
/*
tarjan算法解析: 
https://blog.csdn.net/acmmmm/article/details/16361033
*/ 
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
const int maxm=1e5+10;
int dfn[maxn],low[maxn],ct=0;//dfn[u]为节点u搜索被搜索到时的次序编号(时间戳),
						//low[u]为u或u的子树能够追溯到的最早的栈中节点的次序号 
struct edge{
	int from,v,next;
}e[maxm<<1];
int head[maxn],cnt=0;

int fa[maxn];//fa并查集 
int book[maxn],num[maxn],siz=0;//book染色数组,num表示一个强连通分量中点的个数,siz表示强连通分量的个数
int chu[maxn],in[maxn];//点的出度,入度
bool instack[maxn];//标记是否点在栈中 

stack<int> st;
int n,m;
int w[maxn];//点的权值 
int dp[maxn];

void add(int u,int v){
	e[cnt].from=u;
	e[cnt].v=v;
	e[cnt].next=head[u];
	head[u]=cnt++;
}

void tarjan(int u){//tarjan缩点 
	dfn[u]=low[u]=++ct;
	instack[u] = 1;
	st.push(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(instack[v]){
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(low[u]==dfn[u]){
		siz++;//强连通分量的个数 
		while(1){
			int x = st.top();
			st.pop();
			 
			book[x] = siz;//染色,点x属于第siz个强连通分量
			num[siz]++;//第siz个强连通分量点的个数+1
			
			instack[x] = 0;//标记其不在栈中了 
			fa[x] = u;//x的祖先是u,x和u在一个环里面 
			if(x == u) break;//同一个点就不用再加一遍点权值了 
			w[u]+=w[x];//点u的权值增加
		}
	}
}

int topo(){//拓扑排序+dp 
	queue<int>q;
	int tot=0;
	for (int i=1;i<=n;i++){
		if(fa[i] == i && !in[i]){//当前点入度为0并且他是一个单点,假如到队列中 
			q.push(i);
			dp[i]=w[i];
		}
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for (int i=head[u];~i;i=e[i].next){
			int v=e[i].v;
			dp[v]=max(dp[v],dp[u]+w[v]);
			in[v]--;
			if(!in[v]){
				q.push(v);
			}
		}
	}
	int ans=0;
	for (int i=1;i<=n;i++){
		ans=max(ans,dp[i]); 
	}
	return ans;
}

void init(){//初始化 
	for (int i=1;i<=n;i++){
		fa[i] = i;
	}
	memset(dfn,0,sizeof(dfn));
	memset(instack,0,sizeof(instack));
	memset(head,-1,sizeof(head));
	memset(num,0,sizeof(num));
	memset(book,0,sizeof(book));
	memset(dp,0,sizeof(dp));
	cnt=0;ct=0;siz=0;
}

int main(){
	init();
	cin>>n>>m;
	for (int i=1;i<=n;i++){
		scanf("%d",&w[i]);
	}
	for (int i=1;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);
		}
	}
	
	//缩点之后重新建图 
	cnt=0;
	memset(head,-1,sizeof(head));
	for (int i=0;i<m;i++){//遍历每一条原图的边 
		int x=fa[e[i].from],y=fa[e[i].v];
		if(x!=y){//x点和y点不在一个强联通分量中(不在一个环) 
			in[y]++;
			add(x,y);
		}
	}
	int ans = topo();
	printf("%d\n",ans);
	return 0;
}

割点

点击查看代码块
/*
割点判定: 
1、当根有至少两个儿子时它是割顶 
2、对于儿子点v, 如果不是根的直接儿子,且low[v] >= dfn[u],则它的父亲u是割点
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e4+10;
const int maxm=1e5+10;
int n,m;
struct edge{
	int v,next;
}e[maxm<<1];
int head[maxn],cnt=0;
int ok[maxn];
int dfn[maxn],low[maxn],tot=0;

void add(int u,int v){
	e[cnt].v=v;
	e[cnt].next=head[u];
	head[u]=cnt++;
}

void tarjan(int u,int f){
	dfn[u]=low[u]=++tot;
	int siz=0;//子树的个数 
        int have = 1;
	for (int i=head[u];~i;i=e[i].next){
		int v=e[i].v;
                if(v == f && have) {have = 0;continue;}//无向图的话可以处理重边
		if(!dfn[v]){
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(u==f) siz++;
			//不是根节点且顶点v通过回边能回溯到的最早的(dfn最小的点)没有比dfn[u]更小,u则是割点
			//如果是根节点且子树大小大于等于2,则删去该点后子树之间不能连接
			if((siz>=2 && u==f) || (low[v]>=dfn[u] && u!=f)) ok[u]=1;
		}
		else low[u]=min(low[u],dfn[v]);
	}
}

int main(){
	memset(head,-1,sizeof(head));
	cnt=0;
	cin>>n>>m;
	for (int i=0;i<=n;i++){
		dfn[i]=low[i]=ok[i]=0;
	}
	for (int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);add(v,u);
	}	
	for (int i=1;i<=n;i++){
		if(!dfn[i]) {
			tarjan(i,i);
		}
	}
	
	int ans=0;
	for(int i=1;i<=n;i++){
		if(ok[i]) ans++;
	}
	cout<<ans<<endl;
	for (int i=1;i<=n;i++){
		if(ok[i]) printf("%d ",i);
	}
	return 0;
} 

点击查看代码块
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;

const int maxn = 1e5+10;
vector<int> G[maxn];
int n;
int tot = 0;//tot记录时间戳
int low[maxn],dfn[maxn];
vector<pii> Ans;//存储桥的两点
int bridges = 0;//桥的数量

void init(){
    Ans.clear();
    bridges = 0;
    tot = 0;
    for (int i=0;i<n;i++){
        G[i].clear();
        low[i] = dfn[i] = 0;
    }
}

void tarjan(int u,int f){
    dfn[u] = low[u] = ++tot;
    int have = 1;
    for (int i=0;i<(int)G[u].size();i++){
        int v = G[u][i];
        if(v == f && have){//可以处理重边
          have = 0;continue;
        }
        if(!dfn[v]){
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
            if(low[v] > dfn[u]) {//是桥
                bridges++;
                Ans.push_back(pii(min(u,v),max(u,v)));
            }
        }
        else low[u] = min(low[u],dfn[v]);
    }
}

int main(){
    // freopen("1.out","w",stdout);
    while(~scanf("%d",&n)){
        init();
        for (int i=0;i<n;i++){
            int u;char c;
            scanf("%d%c",&u,&c);
            int num;
            scanf("(%d)",&num);
            for (int j=0;j<num;j++){
                int v;
                scanf("%d",&v);
                G[u].push_back(v);
            }
        }
        for (int i=0;i<n;i++){
            if(!dfn[i]){
                tarjan(i,i);
            }
        }
        sort(Ans.begin(),Ans.end());
        printf("%d critical links\n",bridges);
        for (auto i : Ans){
            int u = i.first,v = i.second;
            printf("%d - %d\n",u,v);
        }
        puts("");
    }
    return 0;
}
/*
8
0 (1) 1
1 (3) 2 0 3
2 (2) 1 3
3 (3) 1 2 4
4 (1) 3
7 (1) 6
6 (1) 7
5 (0)

0

3 critical links
0 - 1
3 - 4
6 - 7

0 critical links

*/

posted @ 2020-08-17 19:41  wsl_lld  阅读(187)  评论(0编辑  收藏  举报