给定一个连通图,问至少要加入几条边使得整个图变成一个边连通图,即图中任意两点都有两条以上的路径(不一定直接相连)。
tarjan算法,设置一个low数组,在建立深搜树的过程中,我们会得到每个节点的low值,对于low值相等的节点在同一个双连通分量中。由于在同一个边连通分量中的点的“地位”是相同的,因此可以将这些双连通分量缩点,就形成一颗无根树。
要让这个无根树变成一个双连通分量,我们需要计算无根树种度为1的节点数平p, 那么答案就是(p+1)/2;
代码:
#include <iostream>
#include <stack>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=5005;
struct Side{
int to,next;
}side[maxn*2];
int node[maxn],dfn[maxn],low[maxn];
bool vis[maxn];
int t,tp,id;
int n,m;
void add_side(int u,int v){
side[t].to=v;
side[t].next=node[u];
node[u]=t++;
}
void tarjan(int u,int fa){
low[u]=dfn[u]=tp++;
vis[u]=true;
for(int i=node[u];i!=-1;i=side[i].next){
int v=side[i].to;
if(v==fa) continue;
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
}
else if(vis[v]){
low[u]=min(low[u],dfn[v]);
}
}
}
int main(){
//freopen("123.txt","r",stdin);
scanf("%d%d",&n,&m);
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(vis,false,sizeof(vis));
memset(node,-1,sizeof(node));
t=0,tp=1,id=0;
while(m--){
int a,b;
scanf("%d%d",&a,&b);
add_side(a,b);
add_side(b,a);
}
tarjan(1,-1);
int leaf[maxn];
memset(leaf,0,sizeof(leaf));
for(int i=1;i<=n;i++){
for(int j=node[i];j!=-1;j=side[j].next){
int v=side[j].to;
if(low[i]!=low[v]){
leaf[low[i]]++;
}
}
}
int sum=0;
for(int i=1;i<=n;i++){
if(leaf[i]==1) sum++;
}
cout<< (sum+1)/2 <<endl;
return 0;
}