poj3352——Road Construction——————【加边为边-双连通图】

来源:互联网 发布:水族馆 知乎 编辑:程序博客网 时间:2024/06/08 07:46

/**

   解题思路:找到图的边-双连通分量,将分量缩成点后,求出缩点后的dfs树的各个结点的度,求出叶子结点(度为1)个数,根据定理计算需要加的最少边即可。

*/

题目大意:

                某度假公司要在可互相到达的各景点(连通图)的一些路上做些装饰,正在装饰的道路暂时不可以通行,现需要架设临时通道,使各个景点仍可相互到达,问最少需要架多少条通道。


/**        关节点或割顶:对于无向图G,如果删除某个点u后,连通分量数目    增加,称u为图的关节点或割顶。        树边:在DFS森林中的边称为树边。        反向边:第一次处理时从后代指向祖先的边称为反向边。        点-双连通图:对于一个连通图,如果任意两点至少存在两条“点不    重复”的路径,则说这个图是点-双连通的。等价于任意两条边都在    同一简单环中,即内部无割顶。        边-双连通图:对于一个连通图,如果任意两点至少存在两条“边不    重复”的路径,则这个图是边-双连通的。等价于每条边都至少在一    个简单环中,即所有边都不是桥。        双连通分量或块:点-双连通的极大子图称为双连通分量或块。        边-双连通分量:边-双连通的极大子图称为边-双连通分量。*/#include<iostream>#include<stdio.h>#include<string.h>#include<algorithm>#include<vector>using namespace std;const int MAXV=1005;vector<int>G[MAXV];           //入无向图int pre[MAXV],degree[MAXV];   //pre是时间戳,degree记录dfs树的各个结点的度int low[MAXV];                //low[v]数组记录结点v及其后代能连回的最早祖先bool iscut[MAXV][MAXV];       //这里没什么用,记录桥int n,m,dfs_clock;            //dfs_clock是一个时钟int min(int x,int y){    return x>=y?y:x;}int dfs(int father,int u){    int lowu=pre[u]=++dfs_clock;            //给定时间戳    for(int i=0;i<G[u].size();i++){        int v=G[u][i];                      //邻接点v        if(!pre[v]){                        //如果儿子v没访问过            int lowv=dfs(u,v);              //计算儿子v的low值            lowu=min(lowu,lowv);            //用后代的low更新u的low            if(lowv>pre[u]){                //如果儿子v的low值大于u的时间戳说明u-v之间是桥                iscut[u][v]=1;                iscut[v][u]=1;            }        }        else if(pre[v]<pre[u]&&v!=father){                 //儿子v已访问过并且是反向边            lowu=min(lowu,pre[v]);          //用反向边更新u的low        }    }    low[u]=lowu;                            //放入数组记录    return lowu;}void init(){    dfs_clock=0;    memset(pre,0,sizeof(pre));    memset(low,0,sizeof(low));    memset(iscut,0,sizeof(iscut));    memset(degree,0,sizeof(degree));    for(int i=1;i<=n;i++){        G[i].clear();    }}int main(){    while(scanf("%d%d",&n,&m)!=EOF){        init();        for(int i=0;i<m;i++){            int tmpa,tmpb;            scanf("%d%d",&tmpa,&tmpb);            G[tmpa].push_back(tmpb);            G[tmpb].push_back(tmpa);            //入图        }        dfs(-1,1);        for(int u=1;u<=n;u++){            for(int j=0;j<G[u].size();j++){            if(low[u]!=low[G[u][j]])            //如果边所连的两个端点不在同一个边-连                                                //通分量中,将该分量的度加1                degree[low[u]]++;            }        }        int leaf=0;        for(int i=1;i<=n;i++){            if(degree[i]==1){                   //如果度为1,说明是叶子结点,记录个数                leaf++;            }        }        /**           这里有一个定理, 将边-双连通分量缩点形成dfs树,叶子(度为1)的个数leaf           在图中最少添加(leaf+1)/2条边,使原图形成没有桥的边-连通图。        */        printf("%d\n",(1+leaf)/2);    }}


0 0
原创粉丝点击