POJ3177/BZOJ1718 Redundant Paths : Tarjan求桥+贪心
Description
考虑一个连通的无向图,可以知道,任意两个节点都可以通过一条路径连接起来。
让你求出要加多少条边使其变成边双连通分量。
Input
第一行N,M.表示N个点M条边的无向图。
后面M行,每行A,B。表示A与B间有一条无向边。
Output
第1行输出一个数S,表示最小加入S条边变成边双连通分量。
Sample Input
7 7
1 2
2 3
3 4
2 5
4 5
5 6
5 7
Sample Output
2
HINT
1<=N<=5000,N-1<=R<=10000
题解
我们可以先根据割边来缩点。这样就可以得到一颗树。
怎么求要加多少边呢?这里有一个贪心,就是每次加一条边将树中的度为1的节点,答案就是(度为1的点的个数+1)/2。
因为每次连接两个叶节点,就把其与祖先构成一个环,最后就没一个点度为0,都是双连通分量。
#include <cstdio>
#include <iostream>
#include <cmath>
#include <stack>
#include <algorithm>
#include <cstring>
#include <climits>
#define MAXN 20000+10
#define MAXM 40000+10
using namespace std;
int head[MAXN],num,n,m;
int dfn[MAXN],low[MAXN],vis[MAXN],dfnum,root,son;
int ans,cnt[MAXM],col[MAXN],co,du[MAXN],tim,f[MAXM];
stack<int> st;
struct Edge{
int from,to,next;
}edge[MAXM*2];
void add(int from,int to)
{
edge[++num].next=head[from];
edge[num].from=from;
edge[num].to=to;
head[from]=num;
}
void tarjan(int x,int fa)
{
dfn[x]=low[x]=++dfnum;
vis[x]=1;
for(int i=head[x];i;i=edge[i].next)
{
if(!dfn[edge[i].to])
{
f[(i+1)/2]=1;
tarjan(edge[i].to,x);
low[x]=min(low[x],low[edge[i].to]);
if(dfn[x]<low[edge[i].to])
cnt[(i+1)/2]=1;
}else
if(vis[edge[i].to]&&!f[(i+1)/2])
low[x]=min(low[x],dfn[edge[i].to]),
f[(i+1)/2]=1;
}
}
void dfs(int x)
{
col[x]=co;
for(int i=head[x];i;i=edge[i].next)
if(!cnt[(i+1)/2]&&!col[edge[i].to])
{
col[edge[i].to]=co;
dfs(edge[i].to);
}
}
int main()
{
int x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i,i);
for(int i=1;i<=n;i++)
if(!col[i]) co++,dfs(i);
for(int i=1;i<=num;i+=2)
{
if(col[edge[i].to]!=col[edge[i].from])
du[col[edge[i].to]]++,du[col[edge[i].from]]++;
}
for(int i=1;i<=co;i++)
{
if(du[i]==1)
ans++;
}
printf("%d\n",(ans+1)/2);
return 0;
}