• 强连通分量(学习心得)


    定义:有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量。

    求强连通分量:

    vector<int>pic[maxn];
    int dfn[maxn],low[maxn],ans[maxn];
    bool ins[maxn];
    stack<int>st;
    int dind=0,block=0;
    int siz[maxn],cud[maxn];
    void tarjan(int x)
    {
    	dind++;
    	dfn[x]=low[x]=dind;
    	ins[x]=true;
    	st.push(x);
    	for (int j=0;j<pic[x].size();j++)
    	{
    		int y=pic[x][j];
    		if (dfn[y]==0)
    		{
    			tarjan(y);
    			low[x]=min(low[x],low[y]);
    		}
    		else if (ins[y]) low[x]=min(low[x],dfn[y]);
    	}
    	if (low[x]==dfn[x])
    	{
    		block++;
    		siz[block]=0;
    		while (true)
    		{
    			int thi=st.top();
    			st.pop();
    			ans[thi]=block;
    			siz[block]++;
    			ins[thi]=false;
    			if (thi==x) break;
    		}
    	}
    }
    

      例题1【UVA-11324 最大团】

    分析:先计算强连通分量,然后建一张新图,每个点的权值是它所包含的点的数量,求图的最长链。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<stack>
    using namespace std;
    const int maxn=1010;
    vector<int> pic[maxn],new_[maxn];
    int dfn[maxn],low[maxn],ans[maxn];
    bool ins[maxn];
    stack<int>st;
    int dind=0,block=0;
    int siz[maxn],dp[maxn],Ans;
    void tarjan(int x)
    {
    	dind++;
    	dfn[x]=low[x]=dind;
    	ins[x]=true;
    	st.push(x);
    	for (int j=0;j<pic[x].size();j++)
    	{
    		int y=pic[x][j];
    		if (!dfn[y]){
    			tarjan(y);
    			low[x]=min(low[x],low[y]);
    		}
    		else if (ins[y]) low[x]=min(low[x],dfn[y]);
    	}
    	if (low[x]==dfn[x])
    	{
    		block++;
    		siz[block]=0;
    		while (true)
    		{
    			int thi=st.top();
    			st.pop();
    			ans[thi]=block;
    			siz[block]++;
    			ins[thi]=false;
    			if (thi==x) break;
    		}
    	}
    }
    int DP(int x){    
        if(dp[x]!=-1) return dp[x];
        int Max=0;  
        for(int i=0;i<new_[x].size();i++)  
            Max=max(Max,DP(new_[x][i]));  
        return dp[x]=siz[x]+Max;
    }
    void init(int n){
    	for(int i=1;i<=n;i++) pic[i].clear(),new_[i].clear();
    	memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low));
    	memset(ans,0,sizeof(ans)); memset(ins,0,sizeof(ins));
    	while(!st.empty()) st.pop(); dind=block=Ans=0;
    	memset(siz,0,sizeof(siz)); memset(dp,-1,sizeof(dp));
    }
    int main(){
    	int n,m,N,u,v;
    	scanf("%d",&N);
    	while(N--){
    		scanf("%d%d",&n,&m);
    		init(n);
    		while(m--){
    			scanf("%d%d",&u,&v);
    			pic[u].push_back(v);
    		}
    		for(int i=1;i<=n;i++)
    		if(!dfn[i]) tarjan(i);
    		for(int i=1;i<=n;i++)
    		for(int j=0;j<pic[i].size();j++)
    		if(ans[i]!=ans[pic[i][j]]) new_[ans[pic[i][j]]].push_back(ans[i]);
    		for(int i=1;i<=block;i++) Ans=max(Ans,DP(i));
    		printf("%d
    ",Ans);
    	}
    	return 0;
    }
    

      例题2【POJ 1236 学校网络】

    第一问:缩点后求入度为0的结点数量;

    第二问:对于一张有向无环图,可以通过把叶子结点连向根节点使它强连通;

    like this:

    所以需要连的边数量为max(叶节点,根节点);

    叶节点是出度为0的点,根节点是入度为0的点;

    注意当只有一个强连通分量时,需要特判,为0;

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<stack>
    #include<set>
    using namespace std;
    const int maxn=110;
    vector<int> pic[maxn];
    int dfn[maxn],low[maxn],ans[maxn];
    bool ins[maxn];
    stack<int>st;
    set<int>check[maxn];
    int dind=0,block=0;
    int siz[maxn];
    int ans1,in[maxn],out[maxn];
    
    void tarjan(int x)
    {
    	dind++;
    	dfn[x]=low[x]=dind;
    	ins[x]=true;
    	st.push(x);
    	for (int j=0;j<pic[x].size();j++)
    	{
    		int y=pic[x][j];
    		if (!dfn[y]){
    			tarjan(y);
    			low[x]=min(low[x],low[y]);
    		}
    		else if (ins[y]) low[x]=min(low[x],dfn[y]);
    	}
    	if (low[x]==dfn[x])
    	{
    		block++;
    		siz[block]=0;
    		while (true)
    		{
    			int thi=st.top();
    			st.pop();
    			ans[thi]=block;
    			siz[block]++;
    			ins[thi]=false;
    			if (thi==x) break;
    		}
    	}
    }
    int main(){
    	int n,i,j;
    	scanf("%d",&n);
    	for(i=1;i<=n;i++){
    		scanf("%d",&j);
    		while(j) pic[i].push_back(j),scanf("%d",&j);	
    	}
    	for(int i=1;i<=n;i++)
    		if(!dfn[i]) tarjan(i);
    	for (i=1;i<=n;i++)
    		for (j=0;j<pic[i].size();j++){
    			int b1=ans[i],b2=ans[pic[i][j]];
    			if (b1==b2) continue;
    			if (check[b1].count(b2)) continue;
    			check[b1].insert(b2);
    			in[b2]++; out[b1]++;
    		}
    	int root=0,leaf=0;
    	for(int i=1;i<=block;i++){
    		if(!in[i]) root++;
    		if(!out[i]) leaf++;
    	}
    	printf("%d
    ",root);
    	if(block==1) printf("0");
    	else printf("%d",max(leaf,root));
    	return 0;
    }
    

      类似的还有【HDU 2767】

    很奇怪的是提交时出现了这样尴尬的问题:

    0_0_20950778_21662.cpp
    0_0_20950778_21662.cpp(29) : error C3861: “min”:  找不到标识符
    0_0_20950778_21662.cpp(31) : error C3861: “min”:  找不到标识符
    0_0_20950778_21662.cpp(83) : error C3861: “max”:  找不到标识符

    然后在出错的库后面加上了#include "minmax.h",就过了?(莫名其妙)

    代码:

    #include<iostream>
    #include "minmax.h" 
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<stack>
    #include<set>
    using namespace std;
    const int maxn=20010;
    vector<int> pic[maxn];
    int dfn[maxn],low[maxn],ans[maxn];
    bool ins[maxn];
    stack<int>st;
    set<int>check[maxn];
    int dind=0,block=0,siz[maxn];
    int in[maxn],out[maxn],T;
    void tarjan(int x)
    {
    	dind++;
    	dfn[x]=low[x]=dind;
    	ins[x]=true;
    	st.push(x);
    	for (int j=0;j<pic[x].size();j++)
    	{
    		int y=pic[x][j];
    		if (!dfn[y]){
    			tarjan(y);
    			low[x]=min(low[x],low[y]);
    		}
    		else if (ins[y]) low[x]=min(low[x],dfn[y]);
    	}
    	if (low[x]==dfn[x])
    	{
    		block++;
    		siz[block]=0;
    		while (true)
    		{
    			int thi=st.top();
    			st.pop();
    			ans[thi]=block;
    			siz[block]++;
    			ins[thi]=false;
    			if (thi==x) break;
    		}
    	}
    }
    void init(int n){
    	for(int i=1;i<=n;i++)  pic[i].clear(),check[i].clear();;
    	memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low));
    	memset(ans,0,sizeof(ans)); memset(ins,0,sizeof(ins));
    	while(!st.empty()) st.pop(); 
    	dind=0;block=0; memset(siz,0,sizeof(siz));
    	memset(in,0,sizeof(in)); memset(out,0,sizeof(out));
    }
    int main(){
    	scanf("%d",&T);
    	while(T--){
    		int n,i,j,m;
    		scanf("%d%d",&n,&m);
    		init(n);
    		for(i=1;i<=m;i++){
    			int x,y;
    			scanf("%d%d",&x,&y);
    			pic[x].push_back(y);	
    		}
    		for(int i=1;i<=n;i++)
    			if(!dfn[i]) tarjan(i);
    		for (i=1;i<=n;i++)
    			for (j=0;j<pic[i].size();j++){
    				int b1=ans[i],b2=ans[pic[i][j]];
    				if (b1==b2) continue;
    				if (check[b1].count(b2)) continue;
    				check[b1].insert(b2);
    				in[b2]++; out[b1]++;
    			}
    		int root=0,leaf=0;
    		for(int i=1;i<=block;i++){
    			if(!in[i]) root++;
    			if(!out[i]) leaf++;
    		}
    		if(block==1) printf("0
    ");
    		else printf("%d
    ",max(leaf,root));
    	}
    	return 0;
    }
    

      ————————————————————————————————————————————————————————

     来自Paper Cloud的博客,未经允许,请勿转载,谢谢

  • 相关阅读:
    多线程与Socket编程
    正则表达式
    委托事件泛型
    C#基础加强
    随笔
    不设置JAVA_HOME运行eclipse
    CentOS7.x系统中使用Docker时,在存储方面需要注意的问题
    【转】关于高可用负载均衡的探索-基于Rancher和Traefic
    Rancher 容器管理平台-免费视频培训-链接及内容-第三季
    使用Rancher的RKE快速部署Kubernetes集群
  • 原文地址:https://www.cnblogs.com/PaperCloud/p/7113385.html
Copyright © 2020-2023  润新知