Tarjan算法求解桥和边双连通分量(附POJ 3352 Road Construction解题报告)
http://blog.csdn.net/geniusluzh/article/details/6619575
在说Tarjan算法解决桥和边双连通分量问题之前我们先来回顾一下Tarjan算法是如何求解强连通分量的。
Tarjan算法在求解强连通分量的时候,通过引入dfs过程中对一个点访问的顺序dfsNum(也就是在访问该点之前已经访问的点的个数)和一个点可以到达的最小的dfsNum的low数组,当我们遇到一个顶点的dfsNum值等于low值,那么该点就是一个强连通分量的根。因为我们在dfs的过程中已经将点仍入到栈中,因此我们只需要将栈中的元素出栈直到遇到根,那么这些点就组成一个强连通分量。
对于边双连通分量,我们需要先了解一些概念:
边连通度:使一个子图不连通所需要删除的最小的边数就是该图的边连通度。
桥(割边):当删除一条边就使得图不连通的那条边称为桥或者是割边。
边双连通分量:边连通度大于等于二的子图称为边双连通分量。
理解了这些概念之后我们来看看Tarjan是如何求解边双连通分量的,不过在此之前我们先说说Tarjan是怎样求桥的。同样引入了dfsNum表示一个点在dfs过程中所被访问的时间,然后就是low数组表示该点最小的可以到达的dfsNum。我们分析一下桥的特点,删除一条边之后,那么如果dfs过程中的子树没有任何一个点可以到达父亲节点及父亲节点以上的节点,那么这个时候子树就被封死了,这条边就是桥。有了这个性质,也就是说当我们dfs过程中遇到一条树边a->b,并且此时low[b]>dfsNum[a],那么a-b就是一座桥。
呵呵桥都求出来了,还怕边双连通分量吗?我们把所有的桥去掉之后那些独立的分量就是不同的边双连通分量,这个时候就可以按照需要灵活的求出边双连通分量了。
下面附上POJ 3352的解题思路吧:
这道题的意思是说,给你一个无向图,然后问你至少需要添加几条边,可以使整个图变成边双连通分量,也就是说任意两点至少有两条路可以互相连通。我们这样考虑这个问题,属于同一个边双连通分量的任意点是至少有两条通路是可以互相可达的,因此我们可以将一个边双连通分量缩成一个点。然后考虑不在边双连通分量中的点,通过缩点后形成的是一棵树。对于一个树型的无向图,需要添加(度为1的点的个数+1)/2条边使得图成为双连通的。这样问题就是变成缩点之后,求图中度为1的点的个数了。
这个题目的条件给的很强,表示任意两个点之间不会有重边,因此我们可以直接经过Tarjan的low值进行边双连通分量的划分,最后求出度为1点数就可以解决问题了。如果是有重边的话,那么不同的low值是可能是属于同一个边双连通分量的,这个时候就要通过将图中的桥去掉然后求解边双连通分量,这个请见我的博客的另外一篇解题报告。
下面贴上POJ 3352的ac代码,供网友们参考:
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
using namespace std;
const int Max=1010;
int top[Max],edge[Max][Max];//memset(top,0,sizeof(top));
int dfsNum[Max],dfsnum;//memset(dfsNum,0,sizeof(dfsNum)),dfsNum=1;
int low[Max];
int degree[Max];
int ans;
void tarjan(int a,int fa)
{
dfsNum[a]=low[a]=++dfsnum;
for(int i=0;i<top[a];i++)
{
if(edge[a][i]!=fa)
{
if(dfsNum[edge[a][i]]==0)
{
tarjan(edge[a][i],a);
if(low[a]>low[edge[a][i]])
low[a]=low[edge[a][i]];
}
else
{
if(low[a]>dfsNum[edge[a][i]])
low[a]=dfsNum[edge[a][i]];
}
// if(low[edge[a][i]]>dfsNum[a])
// {
// }
}
}
}
int solve(int n)
{
int i,j;
int a,b;
for(i=1;i<=n;i++)
{
a=i;
for(j=0;j<top[i];j++)
{
b=edge[a][j];
if(low[a]!=low[b])
{
degree[low[a]]++;
degree[low[b]]++;
}
}
}
int leaves=0;
for(i=1;i<=n;i++)
{
if(degree[i]==2)
{
leaves++;
}
}
return (leaves+1)/2;
}
int main()
{
int n,m;
int i,a,b;
while(scanf("%d %d",&n,&m)!=EOF)
{
memset(top,0,sizeof(top));
memset(degree,0,sizeof(degree));
for(i=0;i<m;i++)
{
scanf("%d %d",&a,&b);
edge[a][top[a]++]=b;
edge[b][top[b]++]=a;
}
memset(dfsNum,0,sizeof(dfsNum));
dfsnum=0;
tarjan(1,-1);
ans=solve(n);
printf("%d\n",ans);
}
return 0;
}
上面的代码的写法确实有问题,因为在同一个双连通分量中的点的low值并不一定相等,所以使用low值来判断是否在同一个分量中显然是有问题的。因此我们最安全的做饭时在tarjan的过程中,把桥标记出来,然后使用dfs跑每一个连通分量,最后使用桥统计每个点的度。
对于上面的问题,给大家带来的误导,非常抱歉。
下面的代码是对上面代码的修改,写的有点乱,供大家分享一下。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
using namespace std;
const int Max=1010;
int top[Max],edge[Max][Max];//memset(top,0,sizeof(top));
int dfsNum[Max],dfsnum;//memset(dfsNum,0,sizeof(dfsNum)),dfsNum=1;
int low[Max];
int degree[Max];
int ans;
bool exist[Max][Max];
void tarjan(int a,int fa)
{
dfsNum[a]=low[a]=++dfsnum;
for(int i=0;i<top[a];i++)
{
if(edge[a][i]!=fa)
{
if(dfsNum[edge[a][i]]==0)
{
tarjan(edge[a][i],a);
if(low[a]>low[edge[a][i]])
low[a]=low[edge[a][i]];
if(dfsNum[a] < low[edge[a][i]])
{
exist[a][edge[a][i]] = exist[edge[a][i]][a] = true;
}
}
else
{
if(low[a]>dfsNum[edge[a][i]])
low[a]=dfsNum[edge[a][i]];
}
}
}
}
int cc[Max], ccCnt;
void dfs(int fa, int u)
{
cc[u] = ccCnt;
for(int i=0; i<top[u]; i++)
{
int v = edge[u][i];
if(v != fa && !exist[u][v] && !cc[v])
{
// printf("v %d\n", v);
dfs(u, v);
}
}
}
int solve(int n)
{
int i,j;
int a,b;
memset(cc, 0, sizeof(cc));
ccCnt = 1;
for(i=1; i<=n; i++)
{
if(!cc[i])
{
dfs(-1, i);
ccCnt++;
}
}
//cout<<"ccCnt "<<ccCnt<<endl;
for(i=1;i<=n;i++)
{
a=i;
for(j=0;j<top[i];j++)
{
b=edge[a][j];
if(cc[a] != cc[b])
{
degree[cc[a]]++;
degree[cc[b]]++;
}
}
}
int leaves=0;
for(i=1;i<ccCnt;i++)
{
if(degree[i]==2)
{
leaves++;
}
}
return (leaves+1)/2;
}
int main()
{
int n,m;
int i,a,b;
while(scanf("%d %d",&n,&m)!=EOF)
{
memset(top,0,sizeof(top));
memset(degree,0,sizeof(degree));
for(i=0;i<m;i++)
{
scanf("%d %d",&a,&b);
edge[a][top[a]++]=b;
edge[b][top[b]++]=a;
}
memset(dfsNum,0,sizeof(dfsNum));
dfsnum=0;
memset(exist, false, sizeof(exist));
tarjan(1,-1);
ans=solve(n);
printf("%d\n",ans);
}
return 0;
}
Tarjan算法求解桥和边双连通分量(附POJ 3352 Road Construction解题报告)的更多相关文章
- 【边双连通】poj 3352 Road Construction
http://poj.org/problem?id=3352 [题意] 给定一个连通的无向图,求最少加多少条边使得这个图变成边双连通图 [AC] //#include<bits/stdc++.h ...
- POJ 3177 Redundant Paths POJ 3352 Road Construction(双连接)
POJ 3177 Redundant Paths POJ 3352 Road Construction 题目链接 题意:两题一样的.一份代码能交.给定一个连通无向图,问加几条边能使得图变成一个双连通图 ...
- POJ 3352 Road Construction(边双连通分量,桥,tarjan)
题解转自http://blog.csdn.net/lyy289065406/article/details/6762370 文中部分思路或定义模糊,重写的红色部分为修改过的. 大致题意: 某个企业 ...
- POJ 3352 Road Construction (边双连通分量)
题目链接 题意 :有一个景点要修路,但是有些景点只有一条路可达,若是修路的话则有些景点就到不了,所以要临时搭一些路,以保证无论哪条路在修都能让游客到达任何一个景点 思路 :把景点看成点,路看成边,看要 ...
- POJ 3352 Road Construction(边—双连通分量)
http://poj.org/problem?id=3352 题意: 给出一个图,求最少要加多少条边,能把该图变成边—双连通. 思路:双连通分量是没有桥的,dfs一遍,计算出每个结点的low值,如果相 ...
- POJ 3177 Redundant Paths & POJ 3352 Road Construction(双连通分量)
Description In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numb ...
- poj 3352 Road Construction【边双连通求最少加多少条边使图双连通&&缩点】
Road Construction Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 10141 Accepted: 503 ...
- POJ 3352 Road Construction 双联通分量 难度:1
http://poj.org/problem?id=3352 有重边的话重边就不被包含在双连通里了 割点不一定连着割边,因为这个图不一定是点连通,所以可能出现反而多增加了双连通分量数的可能 必须要用割 ...
- poj 3352 Road Construction(边双连通分量+缩点)
题目链接:http://poj.org/problem?id=3352 这题和poj 3177 一样,参考http://www.cnblogs.com/frog112111/p/3367039.htm ...
随机推荐
- 20170322js面向对象
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- curses-键盘编码-openssl加解密【转】
本文转载自;https://zhuanlan.zhihu.com/p/26164115 1.1 键盘编码 按键过程:当用户按下某个键时, 1.键盘会检测到这个动作,并通过键盘控制器把扫描码(scan ...
- 使用 Swift 3.0 操控日期
作者:Joe,原文链接,原文日期:2016-09-20译者:Cwift:校对:walkingway:定稿:CMB 当你在想要 大规模重命名 时,一个附带的挑战就是要确保所有相关的文档都必须同步更新.比 ...
- 理解了这些词句涵义用法等,你就熟练ES6了。
let const 块级作用于 暂时性死区 解构赋值:变量的解构赋值.对象的解构赋值.字符串的解构赋值.数值和布尔值的解构赋值. String的扩展 正则表达式的扩展 Number的扩展 Array的 ...
- 图结构练习—BFSDFS—判断可达性(BFS)
http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2138 注意该图为有向图,1000个点应该最多有 ...
- Gym - 101208J 2013 ACM-ICPC World Finals J.Pollution Solution 圆与多边形面积交
题面 题意:给你一个半圆,和另一个多边形(可凹可凸),求面积交 题解:直接上板子,因为其实这个多边形不会穿过这个半圆,所以他和圆的交也就是和半圆的交 打的时候队友说凹的不行,不是板题,后面想想,圆与多 ...
- epoll实现reactor模式
- HTML 简要概述
注: 说实在的,这门语言的确不需要太多或太详细的篇幅来大书特书.掌握个大概,知道些特点及特性也就差不多了.人脑不是电脑,不需要死记硬背许多的属性和值,有一本帮助手册在手,胜过千言万语. 什么是 HTM ...
- C#WebForm里面aspx,ajax请求后台。。。
虽然WebForm里面有那些基本控件,后台CS里面也有许许多多的控件的方法.但是不见得有些标签不需要进行后台的访问,下面介绍一下三种aspx中访问后台的方式.. 第一种:WebMethod (静态方法 ...
- HTML+CSS(10)
n 组合选择器 多元素选择器 n 描述:给多个元素加同一个样式,多个选择器之间用逗号隔开. n 举例:h1,p,div,body{color:red;} 后代元素选择器(最常用) n 描述:给 ...