Description
给定一张5000个点的无向图,要求添加最少的边使得任意两点之间至少有两条不相交的路径
Input
第一行两个整数n和m表示点数和边数,之后m行每行两个整数表示这两点有一条路径
Output
输出添加的最少边数
Sample Input
7 7
1 2
2 3
3 4
2 5
4 5
5 6
5 7
Sample Output
2
Solution
首先用tarjan求出边双连通分量,缩点后统计每个点的度,记ans为度为2的点数,则(ans+1)/2即为答案(因为只需要把缩点后的无向无环图的叶子节点连接起来即可)
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#include<cmath>
#define maxn 5555
using namespace std;
vector<int>g[maxn];
stack<int>st;
int n,m,scc,index;
int low[maxn],dfn[maxn],instack[maxn],fa[maxn];
void init()//初始化
{
scc=index=0;
while(!st.empty())st.pop();
for(int i=0;i<maxn;i++)g[i].clear();
memset(dfn,0,sizeof(dfn));
memset(instack,0,sizeof(instack));
}
void tarjan(int u,int f)//求边双联通分量
{
dfn[u]=low[u]=++index;
instack[u]=1;
st.push(u);
int v,size=g[u].size();
int flag=0;
for(int i=0;i<size;i++)
{
v=g[u][i];
if(v==f&&!flag)
{
flag=1;
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(dfn[u]==low[u])
{
scc++;
do
{
v=st.top();
st.pop();
fa[v]=scc;
instack[v]=0;
}while(v!=u);
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
init();//初始化
for(int i=0;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);//建边
g[v].push_back(u);//建边
}
tarjan(1,1);//求边双联通分量
int cnt[maxn];//记录每个缩点的度
memset(cnt,0,sizeof(cnt));//初始化
for(int i=1;i<=n;i++)//统计每个缩点的度
for(int j=0;j<g[i].size();j++)
{
int v=g[i][j];
if(fa[i]!=fa[v])
{
cnt[fa[i]]++;
cnt[fa[v]]++;
}
}
int ans=0;
for(int i=1;i<=scc;i++)//度为2的缩点数
if(cnt[i]==2)
ans++;
printf("%d\n",(ans+1)/2);
}
return 0;
}