zoukankan      html  css  js  c++  java
  • 图论:割点、缩点、桥

    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
    ",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
    ",bridges);
            for (auto i : Ans){
                int u = i.first,v = i.second;
                printf("%d - %d
    ",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
    
    */
    
    
    你将不再是道具,而是成为人如其名的人
  • 相关阅读:
    Could not update ICEauthority file /var/lib/gdm/.ICEauthority
    反爬虫中技术点的定义
    反爬虫一些问题
    反爬虫准备
    题解「BZOJ4621 Tc605」
    题解「ZJOI2019 语言」
    题解「清华集训2012 序列操作」
    题解「CF1174F Ehab and the Big Finale」
    题解「CF516D Drazil and Morning Exercise」
    题解「HNOI2018 寻宝游戏」
  • 原文地址:https://www.cnblogs.com/wsl-lld/p/13519587.html
Copyright © 2011-2022 走看看