题目链接: http://www.bnuoj.com/v3/problem_show.php?pid=14323
题目大意:有n个牧场, 有m条路相连在一起, 现在牧场主想让任意两个牧场之间都至少有两条不重路的路径, 问最少需要添加几条边。这题需要注意的是有重边
这道题就是求边双联通的
边双联通的定义:任意两点至少村子啊两条“边不重复"的路径;或说每条边都至少在一个简单的环中。
去掉桥,其余的连通分支就是边双连通分支了。一个有桥的连通图要变成边双连通图的话,把双连通子图 收缩为一个点,形成一颗树。需要加的边为(leaf+1)/2 (leaf 为叶子结点个数, 即是节点度为1的个数)
#include<stdio.h>
#include<string.h>
#include<set>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn = 5000 + 10;
set<int> G[maxn];
stack<int> S;
int low[maxn], dfn[maxn], ebc[maxn], bridg, cur, cnt;
void dfs(int u, int fa)
{
low[u] = dfn[u] = ++cur;
S.push(u);
set<int> :: iterator it;
for(it = G[u].begin(); it != G[u].end(); it++)
{
int v = *it;
if(v == fa)
continue;
if(!dfn[v])
{
dfs(v, u);
low[u] = min(low[u], low[v]);
if(low[v] > dfn[u])
bridg++;
}
else if(!ebc[v])
low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u])
{
cnt++;
while(!S.empty())
{
int x = S.top();
S.pop();
ebc[x] = cnt;
if(x == u)
break;
}
}
}
void find_ebc(int n)
{
memset(dfn, 0, sizeof(dfn));
memset(ebc, 0, sizeof(ebc));
cur = cnt = bridg = 0;
dfs(1, -1);
}
int main()
{
int n, m;
int du[maxn];
while(~scanf("%d%d", &n, &m))
{
for(int i = 0; i <= n; i++)
G[i].clear();
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
G[u].insert(v);
G[v].insert(u);
}
find_ebc(n);
memset(du, 0, sizeof(du));
set<int> :: iterator it;
for(int i = 1; i <= n; i++)
for(it = G[i].begin(); it != G[i].end(); it++)
{
int v = *it;
if(ebc[i] != ebc[v])
du[ebc[i]]++;
}
int leaf = 0;
for(int i = 1; i <= cnt; i++)
if(du[i] == 1)
leaf++;
printf("%d\n", (leaf+1)/2);
}
return 0;
}