HDU-3177 Redundant Paths 无向图双连通

来源:互联网 发布:王者荣耀mvp新算法 编辑:程序博客网 时间:2024/06/05 06:47

http://poj.org/problem?id=3177


/*大致题意:       为了保护放牧环境,避免牲畜过度啃咬同一个地方的草皮,牧场主决定利用不断迁移牲畜进行喂养的方法去保护牧草。然而牲畜在迁移过程中也会啃食路上的牧草,所以如果每次迁移都用同一条道路,那么该条道路同样会被啃咬过度而遭受破坏。       现在牧场主拥有F个农场,已知这些农场至少有一条路径连接起来(不一定是直接相连),但从某些农场去另外一些农场,至少有一条路可通行。为了保护道路上的牧草,农场主希望再建造若干条道路,使得每次迁移牲畜时,至少有2种迁移途径,避免重复走上次迁移的道路。已知当前有的R条道路,问农场主至少要新建造几条道路,才能满足要求? 解题思路:“使得每次迁移牲畜时,至少有2种迁移途径,避免重复走上次迁移的道路。”就是说当吧F个农场看作点、路看作边构造一个无向图G时,图G不存在桥。 那么可以建立模型:       给定一个连通的无向图G,至少要添加几条边,才能使其变为双连通图。2.求双连通分量以及构造双连通分量:http://blog.csdn.net/lyy289065406/article/details/6762432对于点双连通分支,实际上在求割点的过程中就能顺便把每个点双连通分支求出。建立一个栈,存储当前双连通分支,在搜索图时,每找到一条树枝边或后向边(非横叉边),就把这条边加入栈中。如果遇到某时满足DFS(u)<=Low(v),说明u是一个割点,同时把边从栈顶一个个取出,直到遇到了边(u,v),取出的这些边与其关联的点,组成一个点双连通分支。割点可以属于多个点双连通分支,其余点和每条边只属于且属于一个点双连通分支。对于边双连通分支,求法更为简单。只需在求出所有的桥以后,把桥边删除,原图变成了多个连通块,则每个连通块就是一个边双连通分支。桥不属于任何一个边双连通分支,其余的边和每个顶点都属于且只属于一个边双连通分支。一个有桥的连通图,如何把它通过加边变成边双连通图?方法为首先求出所有的桥,然后删除这些桥边,剩下的每个连通块都是一个双连通子图。把每个双连通子图收缩为一个顶点,再把桥边加回来,最后的这个图一定是一棵树,边连通度为1。统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf。则至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,所以至少添加的边数就是(leaf+1)/2。具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。*/#include<stdio.h>#include<string.h>#include<vector>using namespace std;const int maxn = 1005;const int inf = 1<<30;int n,m;int time,ans;int low[maxn],dfn[maxn],cut[maxn];bool vis[maxn][maxn];vector<int>map[maxn];void tarjan( int u, int fa ){    low[u] = dfn[u] = ++time;    for( int i = 0; i < map[u].size(); i ++ )    {        int v = map[u][i];        if( v == fa )                     //判定回边            continue;        if( !dfn[v] )        {            tarjan( v,u );            low[u] = low[u] <= low[v] ? low[u]:low[v];        }        else             low[u] = low[u] <= dfn[v] ? low[u]:dfn[v];    }}void output(){memset( cut,0,sizeof(cut) );int num = 0;for( int i = 1; i <= n; i++ ){for( int j = 0; j < map[i].size(); j ++ )         //计算每个点的度数{int v = map[i][j];if( low[v] != low[i] )             //不属于同一个块{cut[low[i]] ++;}}}for( int i = 0; i <= n; i ++ )                  //计算度数为一的点数{if( cut[i] == 1 )num ++;}printf("%d\n",(num+1)/2);}void init()                                        //初始化{    time = 0,ans = inf;    for( int i = 1; i <= n; i ++ )    {            map[i].clear();    }    memset( low,0,sizeof(low) );    memset( dfn,0,sizeof(dfn) );}int main(){    int u,v,d;    while( scanf("%d%d",&n,&m) != EOF  )    {        init();         for( int i = 1; i <= m; i ++ )         {             scanf("%d%d",&u,&v); if( !vis[u][v] ) { map[u].push_back(v); map[v].push_back(u); vis[u][v] = vis[v][u] = 1; } }          tarjan(1,1);  output();    }    return 0;}


//并查集版#include<stdio.h>#include<string.h>#include<vector>using namespace std;const int maxn = 1005;const int inf = 1<<30;int n,m;int time;int low[maxn],dfn[maxn],p[maxn];int bridge[maxn][2],bridge_n;int inq[maxn];vector<int>map[maxn];int find( int x ){return p[x] == x?x:p[x] = find(p[x]);}void merge( int a,int b ){int x = find(a);int y = find(b);if( x == y )return;p[y] = x;}void tarjan( int u, int fa ){int son = 0;              //平行边    low[u] = dfn[u] = ++time;    for( int i = 0; i < map[u].size(); i ++ )    {        int v = map[u][i];        if( v == fa )                     //判定回边            son ++;        if( !dfn[v] )        {            tarjan( v,u );            low[u] = low[u] <= low[v] ? low[u]:low[v];if( !(low[v] > dfn[u]) )    //缩点{merge(v,u);}else{bridge[++bridge_n][0] = u;   //存割边bridge[bridge_n][1] = v;}        }        else if( v != fa || son != 1 )            low[u] = low[u] <= dfn[v] ? low[u]:dfn[v];    }}void output(){memset( inq,0,sizeof(inq) );int ans = 0;for( int i = 1; i <= bridge_n; i++ )  //割边两端的块度数加加{int a = find( bridge[i][0] );int b = find( bridge[i][1] );inq[a] ++;inq[b] ++;}for( int i = 1; i <= n; i ++ )                  //计算度数为一的块数{if( inq[i] == 1 )ans ++;}printf("%d\n",(ans+1)/2);}void init()                                        //初始化{    time = 0,bridge_n = 0;    for( int i = 1; i <= n; i ++ )    {            map[i].clear();p[i] = i;    }memset( bridge,0,sizeof(bridge) );    memset( low,0,sizeof(low) );    memset( dfn,0,sizeof(dfn) );}int main(){    int u,v,d;    while( scanf("%d%d",&n,&m) != EOF  )    {        init();         for( int i = 1; i <= m; i ++ )         {             scanf("%d%d",&u,&v); map[u].push_back(v); map[v].push_back(u); }          tarjan(1,1);  output();    }    return 0;}


原创粉丝点击