双连通分量
题意:一个无向图要添加多少条边才能使其变为边双连通分量,和 poj 3352 Road Construction 几乎一样的题目,不同的是,poj这题,原图是保证连通的,这题是不连通的,过程完全一样,只是最后计算答案的公式不同.所以题目分析就不写了,直接看poj那题吧,其实这题也是模板题,懂双连通分量的知识的话,并不需要看分析
poj那题,缩点后不会出现孤立点,因为整个图连通的,所以只要找到缩点后的叶子个数就可以了,所以是(leaf+1)/2
对于这题,因为图不连通,可能出现缩点后的孤立点。首先看缩点后的图,可能是一块一块的,对于点数超过1的块,和poj那题是一样的,只要找到叶子,所以没找到一个叶子就计数1,然后连接叶子,就能先把这块变成一个边双连通分量。对于那些只有一个点的块,要向其他块连至少2条边才能保证边双连通,所以找到缩点后有多少个孤立点,这些孤立点要计数2,表示连两条边
最后答案就是(A + 1 + B*2)/2 , A表示有多少个叶子,B表示有多少个孤立点
如果原图就是个边双连通分量,不需要加任何边,输出0
同样给出两个代码,第1个代码是简化了tarjan
#include <iostream> #include <cstdio> #include <cstring> #include <stack> #include <vector> using namespace std; #define N 1010 int n,tot; int dfn[N],low[N],vis[N],de[N],dcnt,bcnt; vector<int>e[N]; void init() { dcnt = bcnt = 0; for(int i=1; i<=n; i++) { dfn[i] = 0; e[i].clear(); vis[i] = de[i] = 0; } } void dfs(int u ,int fa) { dfn[u] = low[u] = ++dcnt; vis[u] = 1; for(int i=0; i<e[u].size(); i++) { int v = e[u][i]; if(v == fa) continue; if(!vis[v]) //树边 { dfs(v,u); low[u] = min(low[u] , low[v]); if(low[v] > dfn[u]) //找到一个桥,新增一个连通分量 bcnt++; } else if(vis[v] == 1) //后向边 low[u] = min(low[u] , dfn[v]); } } void solve() { for(int i=1; i<=n; i++) if(!vis[i]) { dfs(i,-1); bcnt++; } if(bcnt == 1) //整个图本身就是个双连通分量 { cout << 0 << endl; return ; } bool used[N]; memset(used,false,sizeof(used)); for(int i=1; i<=n; i++) { if(e[i].size() == 0) //孤立点,它自身形成一个连通分量 { used[low[i]] = true ; continue; } for(int j=0; j<e[i].size(); j++) { int u = i; int v = e[i][j]; used[low[u]] = used[low[v]] = true; if(low[u] != low[v]) //不同的连通分量 de[low[u]]++; } } int res = 0; for(int i=1; i<=n; i++) //扫描缩点后每个点的度 if(used[i] && de[i] == 0) //度为0即孤立点 res += 2; else if(de[i] == 1) //叶子 res++; cout << (res+1)/2 << endl; } int main() { while(cin >> n >> tot) { init(); while(tot--) { int u,v; cin >> u >> v; e[u].push_back(v); e[v].push_back(u); //偷懒直接用vector建图不用静态链表了 } solve(); } return 0; }
模板,找到所有的桥
#include <iostream> #include <cstdio> #include <cstring> #include <stack> #include <vector> #include <utility> using namespace std; #define N 1010 int n,tot; int dfn[N],low[N],ins[N],belong[N],de[N],dcnt,bcnt; vector<int>e[N]; stack<int>sta; typedef pair<int,int> pii; vector<pii>bridge; void init() { dcnt = bcnt = 0; while(!sta.empty()) sta.pop(); bridge.clear(); for(int i=1; i<=n; i++) { dfn[i] = 0; e[i].clear(); ins[i] = de[i] = 0; } } void dfs(int u ,int fa) { sta.push(u); ins[u] = true; dfn[u] = low[u] = ++dcnt; for(int i=0; i<e[u].size(); i++) { int v = e[u][i]; if(v == fa) continue; if(!dfn[v]) //树边 { dfs(v,u); low[u] = min(low[u] , low[v]); if(low[v] > dfn[u]) //桥 { bridge.push_back(make_pair(u,v)); ++bcnt; while(true) { int x = sta.top(); sta.pop(); ins[x] = 0; belong[x] = bcnt; if(x == v) break; } } } else if(ins[v]) //后向边 low[u] = min(low[u] , dfn[v]); } } void solve() { for(int i=1; i<=n; i++) if(!dfn[i]) { dfs(i,-1); bcnt++; while(!sta.empty()) { int x = sta.top(); sta.pop(); ins[x] = 0; belong[x] = bcnt; if(x == i) break; } } if(bcnt == 1) {cout << 0 << endl; return ;} for(int i=0; i<bridge.size(); i++) //取出所有的桥 { int u = bridge[i].first; int v = bridge[i].second; de[belong[u]]++; de[belong[v]]++; } int res = 0; for(int i=1; i<=bcnt; i++) if(de[i] == 0) res += 2; else if(de[i] == 1) res++; cout << (res+1)/2 << endl; } int main() { while(cin >> n >> tot) { init(); while(tot--) { int u,v; cin >> u >> v; e[u].push_back(v); e[v].push_back(u); } solve(); } return 0; }