/*
求边双连通分支:跑一遍求割点与桥的Tarjan得到该图的割点和桥,去掉桥,其余连通分支就是边连通分支了,边连通分支数是桥数+1。
边连通分支就是去掉最少两条边才能将该图划分为两个部分的图,桥就是一个图被去掉一条边就能变成两个子图的那一条边,
构造边双连通分支:
把双连通子图收缩为一个点,形成一个树,需要加边数量是(leaf+1)/ 2 。即不断把最近公共祖先最远的两个叶节点连接一条边
*/
//poj3177
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std ;
const int maxn = 5000 + 10 ;
const int maxm = 20000 + 10 ;
struct Edge{
int to , next ;
//该边是否为桥
bool cut ;
}edge[maxm];
int head[maxn] , tot ;
//Low[u]表示u所能追溯到的在栈中最早的节点,该Low数组和求割点与桥的Low数组定义不一样
int Low[maxn] , DFN[maxn] , Stack[maxn] , Belong[maxn] ;
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 ;
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 , u ) ;
//对于树枝边(u,v),修改low[u]之前一定有何v相连的回向边修改了 Low[v],造成v的Low数值变小
if( Low[u] > Low[v] )
Low[u] = Low[v] ;
//桥边,回溯过后v所能追溯到的标号最小的节点依旧比u的编号大,这时Low[v] = DFN[v] ;
if( Low[v] > DFN[u]){
bridge ++ ;
edge[i].cut = true ;
edge[i ^ 1].cut = true ;
}
}
//(u,v)回向边,修改u所能追溯到的编号最小的节点
else if( Instack[v] && Low[u] > DFN[v])
Low[u] = DFN[v] ;
}
//u节点是该连通分量的根节点
if( Low[u] == DFN[u]){
block ++ ;
do{
v = Stack[ -- top ] ;
Instack[v] = false ;
Belong[v] = block ;
}
while( v != u ) ;
}
}
void ini(){
tot = 0 ;
memset( head , -1 , sizeof( head ) ) ;
}
//缩点后形成树,每个点的度数
int du[maxn] ;
void solve( int n ){
memset( DFN , 0 ,sizeof( DFN )) ;
memset( Instack , false , sizeof( Instack )) ;
Index = top = block = 0 ;
Tarjan( 1 , 0 ) ;
int ans = 0 ;
memset( du , 0 , sizeof( du )) ;
for(int i = 1 ;i<=n ;i++){
for(int j = head[i] ;j!=-1 ; j=edge[j].next )
if( edge[j].cut )
du[Belong [i]] ++;
}
for(int i = 1 ;i<=block ; i++)
if( du [i] == 1)
ans ++;
printf("%d\n" , ( ans + 1) / 2 ) ;
}
int main(){
int n , m ;
int u , v ;
while( scanf("%d%d" , & n , & m ) == 2){
ini() ;
while( m -- ){
scanf("%d%d" , &u , & v ) ;
addedge( u , v ) ;
addedge( v , u ) ;
}
solve( n ) ;
}
}
边双连通分支
最新推荐文章于 2023-01-11 19:17:16 发布