pku 3177 (3352) Redundant Paths
题目大意是,给定一个连通图,要求添加一些边,使每两个顶点之间都有至少两条不相交的路径,求最小需要添加的边数。
很显然,题中要求的图就是一个边双连通图,即边连通度不小于2。我的方法是在原图中DFS求出所有的桥,然后删除这些桥边,剩下的每个连通块都是一个双连通子图。把每个双连通子图收缩为一个顶点,再把桥边加回来,最后的这个图一定是一棵树,边连通度为1。统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf。有人说至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,所以结果就是(leaf+1)/2。
这个结论我不能证明,但是可以想象出是对的。简单说明一下,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。
这道题跟pku3352一模一样,拿这个程序交两个题一个字不用改都能AC!但实际上两个题也稍有不同,这个题有一点很不容易被注意到,就是可能存在重边,两个点之间如果存在有重边,也算是双连通的。我的处理方法是在DFS时标记每条边及其反向边是否被访问过,而不是判断顶点。
/*
* Problem: pku3177 Redundant Paths (USACO JAN06)
* Author: Guo Jiabao
* Time: 2009.4.7 19:37
* State: Solved
* Memo: 桥 双连通分支
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
const int MAXN=5001,MAXM=10001*2*2;
using namespace std;
struct edge
{
edge *next,*op;
int t;
bool vis;
}ES[MAXM];
edge *V[MAXN],*PAR[MAXN];
int N,M,EC=-1,D,Ans;
int DFN[MAXN],LOW[MAXN],Bel[MAXN],Deg[MAXN];
bool adjm[MAXN][MAXN];
inline void addedge(int a,int b)
{
ES[++EC].next=V[a];
V[a]=ES+EC;V[a]->t=b;V[a]->vis=false;
ES[++EC].next=V[b];
V[b]=ES+EC;V[b]->t=a;V[b]->vis=false;
V[a]->op=V[b];V[b]->op=V[a];
}
void init()
{
int i,a,b;
freopen("rpaths.in","r",stdin);
freopen("rpaths.out","w",stdout);
scanf("%d%d",&N,&M);
for (i=1;i<=M;i++)
{
scanf("%d%d",&a,&b);
addedge(a,b);
}
}
void dfs(int u)
{
DFN[u]=LOW[u]=++D;
for (edge *k=V[u];k;k=k->next)
{
int v=k->t;
if (k->vis) continue;
k->vis=k->op->vis=true;
if (!DFN[v])
{
PAR[v]=k->op;
dfs(v);
if (LOW[v]<LOW[u])
LOW[u]=LOW[v];
}
else if (DFN[v]<LOW[u])
LOW[u]=DFN[v];
}
}
void part(int u)
{
Bel[u]=D;
for (edge *k=V[u];k;k=k->next)
if (k->vis && !Bel[k->t])
part(k->t);
}
void solve()
{
int i,u,v,leaf=0;
edge *k;
dfs(1);
for (v=2;v<=N;v++)
{
k=PAR[v];
u=k->t;
if (DFN[u]<LOW[v])
k->vis=k->op->vis=false;//标记为桥边
}
D=0;
for (u=1;u<=N;u++)
{
if (!Bel[u])
{
++D;
part(u);
}
}
for (u=1;u<=N;u++)
for (k=V[u];k;k=k->next)
adjm[Bel[u]][Bel[k->t]]=adjm[Bel[k->t]][Bel[u]]=true;
for (u=1;u<=D;u++)
for (v=1;v<=D;v++)
if (u!=v && adjm[u][v])
Deg[u]++;
for (i=1;i<=D;i++)
if (Deg[i]==1)
leaf++;
if (leaf!=1)
Ans=(leaf+1)/2;
else
Ans=0;
}
int main()
{
init();
solve();
printf("%dn",Ans);
return 0;
}