POJ--3352(边双连通分量,tarjan)
2015-05-06 23:12:52
题目:给出无向图,让你求出至少要加多少条边才能使之成为边双连通图。
思路:按照大白书的思路打的... 首先通过简易 tarjan 求出所有桥,标记一下。然后 dfs 跑图,不经过桥,从而找出所有边双连通分量。
将所有边双连通分量缩点后,加上桥,就形成一棵树,那么所要加的边数就是 (叶子数 + 1)/ 2。
【简证:对于一对叶子a,b,在它们之间建边可以形成一个环:a -> b -> lca -> a,即达成边双连通。
如果处理所有的叶子对,那么整个图就是环与环的叠加、邻接,显然双连通。
为了处理所有叶子,至少加边:(叶子数+1)/ 2】
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 1010; int first[MAXN],ecnt; int n,m,bg[MAXN << 1],deg[MAXN]; int dfn[MAXN],low[MAXN],bcnt,bcc[MAXN],tot; struct edge{ int v,next; }e[MAXN << 1]; void Init(){ memset(deg,0,sizeof(deg)); memset(bg,0,sizeof(bg)); memset(first,-1,sizeof(first)); ecnt = bcnt = tot = 0; } void add_edge(int u,int v){ e[ecnt].next = first[u]; e[ecnt].v = v; first[u] = ecnt++; } void Dfs(int p,int pre){ //find bridge dfn[p] = low[p] = ++tot; for(int i = first[p]; ~i; i = e[i].next){ int v = e[i].v; if(v == pre) continue; if(!dfn[v]){ Dfs(v,p); low[p] = min(low[p],low[v]); if(low[v] > dfn[p]) bg[i] = 1; } else low[p] = min(low[p],dfn[v]); } } void Tarjan(){ memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); for(int i = 1; i <= n; ++i) if(!dfn[i]) Dfs(i,0); } void Dfs2(int p,int pre){ bcc[p] = bcnt; for(int i = first[p]; ~i; i = e[i].next){ int v = e[i].v; if(bcc[v] || bg[i]) continue; Dfs2(v,p); } } void BCC(){ memset(bcc,0,sizeof(bcc)); for(int i = 1; i <= n; ++i) if(!bcc[i]){ ++bcnt; Dfs2(i,0); } } int main(){ int a,b; while(scanf("%d%d",&n,&m) != EOF){ Init(); for(int i = 1; i <= m; ++i){ scanf("%d%d",&a,&b); add_edge(a,b); add_edge(b,a); } Tarjan(); BCC(); for(int i = 1; i <= n; ++i){ for(int j = first[i]; ~j; j = e[j].next){ int v = e[j].v; if(bcc[i] != bcc[v]) deg[bcc[v]]++; } } int cnt = 0; for(int i = 1; i <= bcnt; ++i) if(deg[i] == 1) ++cnt; printf("%d\n",(cnt + 1) / 2); } return 0; }