这两题是一样的,代码完全一样。
就是给了一个连通图,问加多少条边可以变成边双连通。
去掉桥,其余的连通分支就是边双连通分支了。一个有桥的连通图要变成边双连通图的话,把双连通子图收缩为一个点,形成一颗树。需要加的边为(leaf+1)/2 (leaf为叶子结点个数)
POJ 3177 给定一个连通的无向图G,至少要添加几条边,才能使其变为双连通图。
参考:http://blog.csdn.net/lyy289065406/article/details/6762432
http://www.cnblogs.com/kuangbin/p/3184889.html
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <map> using namespace std; const int MAXN = 5010;//点数 const int MAXM = 20010;//边数,因为是无向图,所以这个值要*2 struct Edge { int to,next; bool cut;//是否是桥标记 }edge[MAXM]; int head[MAXN],tot; int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];//Belong数组的值是1~block 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); if( Low[u] > Low[v] )Low[u] = Low[v]; if(Low[v] > DFN[u]) { bridge++; edge[i].cut = true; edge[i^1].cut = true; } } else if( Instack[v] && Low[u] > DFN[v] ) Low[u] = DFN[v]; } if(Low[u] == DFN[u]) { block++; do { v = Stack[--top]; Instack[v] = false; Belong[v] = block; } while( v!=u ); } } void init() { 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++; //找叶子结点的个数ans,构造边双连通图需要加边(ans+1)/2 printf("%d\n",(ans+1)/2); } int main() { int n,m; int u,v; while(scanf("%d%d",&n,&m)==2) { init(); while(m--) { scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } solve(n); } return 0; }