POJ3177.Redundant Paths——增加多少条边使原图变为边双连通图
来源:互联网 发布:西门子plc300编程实例 编辑:程序博客网 时间:2024/06/07 22:36
http://poj.org/problem?id=3177
题目描述:
有n个牧场,Bessie 要从一个牧场到另一个牧场,要求至少要有2条独立的路可以走。现已有m条路,求至少要新建多少条路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指:没有公共边的路,但可以经过同一个中间顶点。
分析:在同一个边双连通分量中,任意两点都有至少两条独立路可达,所以同一个边双连通分量里的所有点可以看做同一个点。
缩点后,新图是一棵树,树的边就是原无向图的桥。
现在问题转化为:在树中至少添加多少条边能使图变为双连通图。
结论:添加边数=(树中度为1的节点数+1)/2
具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。
其实求边双连通分量和求强连通分量差不多,每次访问点的时候将其入栈,当low[u]==dfn[u]时就说明找到了一个连通的块,则栈内的所有点都属于同一个边双连通分量,因为无向图要见反向边,所以在求边双连通分量的时候,遇到反向边跳过就行了。
另外如果low[v]<=dfn[u]说明u,v在同一个边双连通分量中,可以用并查集合并
//220K 0MS C++#include <cstdio>#include <cstring>#include <cstdlib>#include <iostream>#define clr(a) memset(a,0,sizeof(a))#define MIN(a,b) ((a)>(b)?(b):(a))#define N 1005#define M 20005using namespace std;typedef struct NodeStr { //边结点 int j; //j 为另一个顶点的序号 struct NodeStr *next; //下一个边结点} Node;int n, m; //顶点数、边数Node mem[M];int memp; //mem 为存储边结点的数组,memp 为mem 数组中的序号Node *e[N]; //邻接表int w; //原图中边双连通分量的个数int belong[N];int low[N], dfn[N]; //low[i]为顶点i 可达祖先的最小编号,dfn[i]为深度优先数int visited[N]; //visited[i]为0-未访问,为1-已访问,为2-已访问且已检查邻接顶点int bridge[M][2], nbridge;void addEdge( Node *e[], int i, int j ) //在邻接表中插入边(i,j){ Node *p = &mem[memp++]; p->j = j; p->next = e[i]; e[i] = p;}int FindSet( int f[], int i ) //并查集的查找函数{ int j = i, t; while( f[j]!=j ) j = f[j]; while( f[i]!=i ) { t = f[i]; f[i] = j; i = t; } return j;}void UniteSet( int f[], int i, int j ) //并查集的合并函数{ int p = FindSet(f,i), q = FindSet(f,j); if( p!=q ) f[p] = q;}void DFS_2conn( int i, int father, int dth, int f[] ){ int j, tofather = 0; Node *p; visited[i] = 1; low[i] = dfn[i] = dth; for( p=e[i]; p!=NULL; p=p->next ) { j = p->j; if( visited[j]==1 && (j!=father||tofather) ){ low[i] = MIN(low[i],dfn[j]); } if( visited[j]==0 ) { DFS_2conn( j, i, dth+1, f ); low[i] = MIN( low[i], low[j] ); if( low[j]<=dfn[i] ) UniteSet( f, i, j ); //i,j 在同一个双连通分量 if( low[j]>dfn[i] ) //边(i,j)是桥 bridge[nbridge][0] = i, bridge[nbridge++][1] = j; } if( j==father ) { tofather = 1; } } visited[i] = 2;}//求无向图极大边双连通分量的个数int DoubleConnection( ){ int i, k, f[N],ncon = 0; for( i=0; i<n; i++ ) f[i] = i, belong[i] = -1; //f[]并查集数组 clr( visited ); nbridge = 0; DFS_2conn( 0, -1, 1, f ); cout<<"bridge="<<nbridge<<endl; for( i=0; i<n; i++ ) { k = FindSet( f, i ); if( belong[k]==-1 ) belong[k] = ncon++; belong[i] = belong[k]; } return ncon;}int main( ){#ifndef ONLINE_JUDGEfreopen("in.cpp","r",stdin);#endif // ONLINE_JUDGE int i, j, k; while( scanf( "%d%d", &n, &m ) != EOF ) { memp = 0; clr(e); for( k=0; k<m; k++ ) { //读入边,并插入邻接表中 scanf( "%d%d", &i, &j ); i--; j--; addEdge( e, i, j ); addEdge( e, j, i ); } w = DoubleConnection( ); //求边双连通分量个数 int d[N] = { 0 }; //收缩后各顶点的度数 for( k=0; k<nbridge; k++ ) { i = bridge[k][0]; j = bridge[k][1]; d[belong[i]]++; d[belong[j]]++; } int count = 0; //收缩后叶子结点的个数 for( i=0; i<w; i++ ) if( d[i]==1 ) count++; printf( "%d\n", (count+1)/2 ); } return 0;}
0 0
- POJ3177.Redundant Paths——增加多少条边使原图变为边双连通图
- poj3177 Redundant Paths 边双连通分量
- poj3177——Redundant Paths(双连通分量)
- poj3177——Redundant Paths(双连通分量)
- poj3177 Redundant Paths(边双连通分量+缩点)
- [POJ3177]Redundant Paths 边双连通分量 做题笔记
- 【POJ3177】Redundant Paths-边双连通分量+缩点
- poj3177 Redundant Paths 无向图 双连通分量 桥
- poj 3177 Redundant Paths(求最少加几条边将图变为边双连通图)
- 【poj3177】【双连通分量】Redundant Paths
- poj3177 Redundant Paths 双连通分量
- poj3177 Redundant Paths 双连通分量
- poj3177——Redundant Paths//最小割边
- poj3177—Redundant Paths(缩点+边双联通分量)
- poj3177&&poj3352 无向图的双连通问题(至少加几条边原图变为双连通图)
- poj 3177 Redundant Paths 【无向图增加最少的边是图成为边—双连通】【tarjan求EBC + 缩点 统计度数为1的EBC】
- POJ3177 Redundant Paths 3352的双胞胎题 双连通分量
- Redundant Paths-POJ3177(并查集+双连通分量)
- HDU 1039 — Easier Done Than Said?
- Insertion Sort List
- Linux Core Dump
- 黑马程序员—【Java基础篇】之IO流(一)
- 前端开发者喜欢的20个学习文档和指南
- POJ3177.Redundant Paths——增加多少条边使原图变为边双连通图
- Insertion Sort List
- 关于android adt的更新
- [java基础学习]05——方法和数组
- 服务器不能解析php
- 工厂与商品分类的关系-Correlation Between Sites and Merchandise Categories
- 反向迭代器(rbegin,rend)
- 【中级】双链表基本操作
- 第3题