• 连通图模板


    强连通分量

    模板:(output用来在原来不是强连通的基础上求加多少边变为强连通)

    矩阵存储

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define MAXV 110
    #define min(a,b) (a>b?b:a)
    #define max(a,b) (a>b?a:b)
    
    int n,map[MAXV][MAXV],outdegree[MAXV],indegree[MAXV];
    int dfn[MAXV];									//第一次访问的步数
    int low[MAXV];									//子树中最早的步数
    int stap[MAXV],stop;							//模拟栈
    bool instack[MAXV];								//是否在栈中
    int count;										//记录连通分量的个数
    int cnt;										//记录搜索步数
    int belong[MAXV];								//属于哪个连通分量
    
    void init(){
    	count=stop=cnt=0;
    	memset(instack,false,sizeof(instack));
    	memset(map,0,sizeof(map));
    	memset(dfn,0,sizeof(dfn));
    }
    
    void tarjan(int x){
    	int i;
    	dfn[x]=low[x]=++cnt;
    	stap[stop++]=x;
    	instack[x]=true;
    	for(i=1;i<=n;i++){
    		if(!map[x][i]) continue;
    		if(!dfn[i]){
    			tarjan(i);
    			low[x]=min(low[i],low[x]);
    		}else if(instack[i])
    			low[x]=min(dfn[i],low[x]);
    		//与x相连,但是i已经被访问过,且还在栈中
            //用子树节点更新节点第一次出现的时间
    	}
    
    	if(low[x]==dfn[x]){
    		count++;
    		while(1){
    			int tmp=stap[--stop];
    			belong[tmp]=count;
    			instack[tmp]=false;
    			if(tmp==x) break;
    		}
    	}
    }
    
    void output(){
    	int i,j,inzero=0,outzero=0;
    	for(i=1;i<=n;i++){
    		indegree[i]=outdegree[i]=0;
    	}
    	for(i=1;i<=n;i++)				//找连通分量入度与出度
    		for(j=1;j<=n;j++)
    			if(map[i][j] && belong[i]!=belong[j]){
    				indegree[belong[j]]++;
    				outdegree[belong[i]]++;
    			}
    	for(i=1;i<=count;i++){			//找入度与出度为0的点
    		if(!indegree[i]) inzero++;
    		if(!outdegree[i]) outzero++;
    	}
    	
    
    	if(count==1)					//只有1个结点要特判
    		printf("1
    0
    ");
    	else
    		printf("%d
    %d
    ",inzero,max(inzero,outzero));
    }
    
    int main(){
    	int i,a;
    	while(~scanf("%d",&n)){
    		init();
    		for(i=1;i<=n;i++){
    			while(scanf("%d",&a) && a) map[i][a]=1;
    		}
    		for(i=1;i<=n;i++)
    			if(!dfn[i])	tarjan(i);
    		output();
    	}
    	return 0;
    }
    邻接表存储:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define MOD 100000
    #define inf 1<<29
    #define LL long long
    #define MAXN 20010
    #define MAXM = 50010
    using namespace std;
    struct Edge
    {
        int to,next;
        bool cut;
    } edge[MAXN];
    
    
    int head[MAXN],tot;
    int low[MAXN],DFN[MAXN],belong[MAXN];///belong 的值为1-block
    int index,top,fenzhiNum;
    int block ;  ///强连通分量
    bool inStack[MAXN];
    int bridgeNum;  ///桥的数目
    int stack[MAXN];
    int vis[MAXN];
    int inans,outans;
    int outdu[MAXN];
    int indu[MAXN];
    
    void addedge(int u,int v)
    {
        edge[tot].to = v;
        edge[tot].next = head[u];
        edge[tot].cut = false;
        head[u] = tot++ ;
    }
    void ini(){
        index = block = top = fenzhiNum = 0;
        inans = 0, outans = 0 ;
        memset(DFN,0,sizeof(DFN));
        memset(inStack,false,sizeof(inStack));
        memset(vis,0,sizeof(vis));
        memset(outdu,0,sizeof(outdu));
        memset(indu,0,sizeof(indu));
        memset(head,-1,sizeof(head));
    }
    void Tarjan(int u)
    {
        vis[u] = true;
        int v;
        low[u] = DFN[u] = ++index;
        stack[top++] = u;
        inStack[u] = true;
        for(int i=head[u] ; i!=-1 ; i=edge[i].next)
        {
            v = edge[i].to;
            //if( v == pre ) continue;    ///因为是无向图,所以两条是双向的,所以只遍历一条就够了
            if( !DFN[v] )
            {
                Tarjan(v );
                if(low[u]>low[v])
                    low[u] = low[v];
                if(low[v] > DFN[u] ){
                    bridgeNum++;
                    edge[i].cut = true;
                    //edge[i^1].cut = true;  ///将两条双向边都设置为桥
                }
    
            }
            else if( inStack[v] && low[u] > DFN[v])
                low[u] = DFN[v];
        }
        if(low[u] == DFN[u])
        {
            block++;
            do
            {
                v=stack[--top];  ///清空当前强连通分量栈 必须清空
                inStack[v] = false;
                belong[v]=block;   ///v节点都编号为block  也就是这是一个块
            }
            while(v!=u);
        }
    }
    
    void solve(int N)
    {
        for(int i=1;i<=N;i++)
            if(!vis[i])
                Tarjan(i);
        for(int i=1; i<=N ; i++){  ///缩点
            for(int j=head[i] ; j!=-1 ; j=edge[j].next)
              if( belong[i]!=belong[ edge[j].to ] )
                indu[ belong[ edge[j].to ] ]++,outdu[ belong[i] ]++ ;
        }
        for(int i=1;i<=block ;i++)
            if(indu[i] == 0)
               inans++;
        for(int i=1;i<=block ;i++)
            if(outdu[i] == 0)
               outans++;
       // printf("indu=%d,outdu=%d
    ",inans,outans);
       if(block == 1) printf("1
    0
    ");
       else printf("%d
    %d
    ",inans,max(inans,outans));
        //printf("%d
    ",(ans+1)/2 );
    }
    
    int main ()
    {
        int n,m;
        while(~scanf("%d",&n))
        {
            int u,v,mark=0;
            tot=0;
            ini();
            for(int i=1; i<=n; i++)
            {
                while(scanf("%d",&u)&&u!=0){
                  mark=0;
                  for(int j=head[i] ; j!=-1 ; j=edge[j].next)  ///去重边
                    if(edge[j].to == u){
                        mark = 1;
                        break;
                    }
                  if(!mark) addedge(i,u);
                }
           }
            solve(n);
        }
        return 0;
    }



    割点与桥

    模板:

    /*
    *  求  无向图   的割点和桥
    *  可以找出割点和桥,求删掉每个点后增加的连通块。
    *  需要注意重边的处理,可以先用矩阵存,再转邻接表,或者进行判重
    */
    const int MAXN = 10010;
    const int MAXM = 2000010;
    struct Edge
    {
        int to,next;
        bool cut;//是否为桥的标记
    }edge[MAXM];
    int head[MAXN],tot;
    int Low[MAXN],DFN[MAXN],Stack[MAXN];
    int Index,top;
    bool Instack[MAXN];
    bool cut[MAXN];   ///记录是否是割点
    int add_block[MAXN];//删除一个点(i)后增加的连通块
    int bridge;
    
    void addedge(int u,int v)
    {
        edge[tot].to = v;edge[tot].next = head[u];edge[tot].cut = false;
        head[u] = tot++;
    }
    
    
    void Tarjan(int u,int pre)  ///pre是父节点,用来判断重边
    {
        int v;
        Low[u] = DFN[u] = ++Index;
        Stack[top++] = u;
        Instack[u] = true;
        int son = 0;
        int pre_cnt = 0;  ///处理重边 ,如果不需要可以去掉
        for(int i = head[u];i != -1;i = edge[i].next)
        {
            v = edge[i].to;
            if(v == pre && pre_cnt == 0)
            {
                pre_cnt++;
                continue;
            }
            if( !DFN[v] )
            {
                son++;
                Tarjan(v,u);
                if(Low[u] > Low[v])
                    Low[u] = Low[v];
                ///桥
                ///一条无向边(u,v)是桥,当且仅当(u,v)为树枝边,且满足DFS(u)<Low(v)。
                if(Low[v] > DFN[u])
                {
                    bridge++;
                    edge[i].cut = true;
                    edge[i^1].cut = true;
                }
                //割点
                //一个顶点u是割点,当且仅当满足(1)或(2) 
                //(1) u为树根,且u有多于一个子树。
                //(2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得DFS(u)<=Low(v)
                if(u != pre && Low[v] >= DFN[u])//不是树根
                {
                    cut[u] = true;
                    add_block[u]++;
                }
            }
            else if( Low[u] > DFN[v])
                 Low[u] = DFN[v];
        }
        //树根,分支数大于1
        if(u == pre && son > 1)
            cut[u] = true;
        if(u == pre)
           add_block[u] = son - 1;
        Instack[u] = false;
        top--;
    }

    边双连通分量(也就是求桥的个数,这不过这道题比较经典)

    //#pragma comment(linker, "/STACK:102400000,102400000"
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<queue>
    #include<stack>
    #include<string>
    #include<map>
    #include<set>
    #include<ctime>
    #define eps 1e-6
    #define MAX 100005
    #define INF 0x3f3f3f3f
    #define LL long long
    #define pii pair<int,int>
    #define rd(x) scanf("%d",&x)
    #define rd2(x,y) scanf("%d%d",&x,&y)
    using namespace std;
    
    const int MAXN = 200010;//点数
    const int MAXM = 2000010;//边数,因为是无向图,所以这个值要*2
    struct Edge
    {
        int to,next;
        bool cut;//是否是桥标记
    }edge[MAXM];
    int head[MAXN],tot;
    int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];//Belong数组的值是1~block
    int Index,top;
    int block;//边双连通块数
    bool Instack[MAXN];
    int bridge;//桥的数目
    
    void addedge(int u,int v)
    {
        edge[tot].to = v;edge[tot].next = head[u];edge[tot].cut=false;
        head[u] = tot++;
    }
    
    void Tarjan(int u,int pre)
    {
        int v;
        Low[u] = DFN[u] = ++Index;
        Stack[top++] = u;
        int pre_cnt=0;  ///处理重边
        Instack[u] = true;
        for(int i = head[u];i != -1;i = edge[i].next)
        {
            v = edge[i].to;
            if(v == pre && pre_cnt == 0 ){
                    pre_cnt ++;
                    continue;
            }
            if( !DFN[v] )
            {
                Tarjan(v,u);
                if( Low[u] > Low[v] )Low[u] = Low[v];
                if(Low[v] > DFN[u])
                {
                    bridge++;
                    edge[i].cut = true;
                    edge[i^1].cut = true;
                }
            }
            else if( Instack[v] && Low[u] > DFN[v] )
                Low[u] = DFN[v];
        }
        if(Low[u] == DFN[u])
        {
            block++;
            do
            {
                v = Stack[--top];
                Instack[v] = false;
                Belong[v] = block;
            }
            while( v!=u );
        }
    }
    void init()
    {
        tot = 0;
        memset(head,-1,sizeof(head));
    }
    
    int du[MAXN];//缩点后形成树,每个点的度数
    vector<int>vec[MAXN];
    int dep[MAXN];
    void dfs(int u)
    {
        for(int i = 0;i < vec[u].size();i++)
        {
            int v = vec[u][i];
            if(dep[v]!=-1)continue;
            dep[v]=dep[u]+1;
            dfs(v);
        }
    }
    void solve(int n)
    {
        memset(DFN,0,sizeof(DFN));
        memset(Instack,false,sizeof(Instack));
        Index = top = block = 0;
        Tarjan(1,0);
        for(int i = 1;i <= block;i++)
            vec[i].clear();
        for(int i = 1;i <= n;i++)
           for(int j = head[i];j != -1;j = edge[j].next)
              if(edge[j].cut)
              {
                  vec[Belong[i]].push_back(Belong[edge[j].to]);
              }
        memset(dep,-1,sizeof(dep));
        dep[1]=0;
        dfs(1); ///第一次dfs找距离1节点最远的节点k
        int k = 1;
        for(int i = 1;i <= block;i++)
            if(dep[i]>dep[k])
              k = i;
        memset(dep,-1,sizeof(dep));
        dep[k]=0;
        dfs(k);  ///第二次dfs找出距离k最远的节点,也就是树的直径
        int ans = 0;
        for(int i = 1;i <= block;i++)
            ans = max(ans,dep[i]);
        printf("%d
    ",block-1-ans);
    }
    int main()
    {
        //freopen("in.txt","r",stdin);
        //freopen("out.txt","w",stdout);
        int n,m;
        int u,v;
        while(scanf("%d%d",&n,&m)==2)
        {
            if(n==0 && m==0)break;
            init();
            for(int i = 0;i < m;i++)
            {
                scanf("%d%d",&u,&v);
                addedge(u,v);
                addedge(v,u);
            }
            solve(n);
        }
        return 0;
    }
    




    构造双连通图
    模板:

  • 相关阅读:
    程序员必看书籍(转载)
    JBPM的ORACLE脚本
    XFire构建web service客户端的五种方式
    为什么中国出不了facebook和Twitter?
    用dwr封装表单项提交表单
    Java 程序员容易犯的10个SQL错误
    SQL语句优化方法30例
    sqlserver sql语句查看分区记录数、查看记录所在分区
    SQL Case when 的使用方法
    sqlserver sql语句附加 分离数据库
  • 原文地址:https://www.cnblogs.com/zswbky/p/6717969.html
Copyright © 2020-2023  润新知