http://poj.org/problem?id=3177
题意:有F个牧场(1<=F<=5000),现在一个牧群经常需要从一个牧场迁移到另一个牧场。奶牛们已经厌烦老是走同一条路,所以有必要再新修几条路,这样它们从一个牧场迁移到另一个牧场时总是可以选择至少两条独立的路。现在F个牧场的任何两个牧场之间已经至少有一条路了,奶牛们需要至少有两条。给定现有的R条(F-1<=R<=10000)直接连接两个牧场的路,计算至少需要新修多少条直接连接两个牧场的路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指没有公共边的路,但可以经过同一个中间顶点。
分析:题目可转换为对于给定的无向连通图,至少添加几条边使它变成一个边双连通图。用tarjan算法求出所有的边连通分量,并将其缩成点构成一棵树,要想让这些缩点之间有至少两条不共边的路径,只需将所有叶节点(度为1的节点)连接起来即可,至少要连(叶节点+1)/2条边。
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 5005, maxm = 10005;
int cnt, n, m, e, index,top;//cnt记录边连通分量序号,index记录遍历序列
int first[maxn], dfn[maxn], low[maxn];
int belong[maxn], out[maxn],stack[maxn];
bool inStack[maxn];
struct Edge {
int u, v, next;
Edge(){}
Edge(int u,int v,int next):u(u),v(v),next(next){}
}edges[maxm*2];
void init()
{
memset(first, -1, sizeof(first));
e = index = cnt = top = 0;
}
void addEdge(int u, int v)
{
edges[e] = Edge(u, v, first[u]);
first[u] = e++;
edges[e] = Edge(v, u, first[v]);
first[v] = e++;
}
void tarjan(int u,int fa)
{
inStack[u] = true;
stack[top++] = u;
dfn[u] = low[u] = ++index;
bool flag = true;
for (int i = first[u]; i != -1; i = edges[i].next)
{
int v = edges[i].v;
if (v == fa&&flag)//判断是否为重边,第一次跳过,之后的要走
{
flag = false;
continue;
}
if (!dfn[v])
{
tarjan(v, u);
low[u] = min(low[u], low[v]);
}
else if (inStack[v])
low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u])
{
cnt++;
int v;
do {
v = stack[--top];
belong[v] = cnt;
inStack[v] = false;
} while (v != u);
}
}
int main()
{
while (~scanf("%d%d", &n, &m))
{
init();
for (int i = 0; i < m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
addEdge(u, v);
}
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i,-1);
for (int i = 0; i < e; i++)
{
Edge p = edges[i];
if (belong[p.u] != belong[p.v])//计算出度
out[belong[p.u]]++;
}
int leaves=0;
for (int i = 1; i <= cnt; i++)
if (out[i]==1)
{
leaves++;
}
cout << (leaves + 1) / 2 << endl;
}
return 0;
}