题目描述:
有F个牧场(F<=5000),R条路(F-1<=R<=10000),题目保证各个牧场互相连通,现在要求使得任意两个牧场之间至少有两条不同的道路可走(可经过相同点不经过相同边)。
求至少需要修多少条新的路。
题目分析:
转化为有无图。根据边双连通分量的定义可知,一个边双连通分量内的牧场之间都满足有两条不同的路互相到达,所以只需要将全部的点连接成一个边双连通分量即可。
tarjin算法对题目给出的无向图进行边双连通分量缩点,缩点完成之后统计度为1的边双联通分量的个数N,那么需要添加(N+1)/2条边才能将整个图变为边双连通分量。
例:如图,圆圈表示的是每一个边双连通分量,有5个边双连通分量需要连接3条边
代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
using namespace std;
const int N = 1e5 + 50;
int vis[N], low[N], dfn[N], cnt[N], belong[N];
bool mp[5050][5050];
int E, deep, n, m, cnt2;
stack<int>stk;
void dfs(int u, int fa)
{
vis[u] = 1; //在栈中
stk.push(u);
low[u] = dfn[u] = ++deep;
for (int i = 1; i <= n; i++)
{
if (mp[u][i] && i != fa)
{
if (!vis[i]){
dfs(i, u);
low[u] = min(low[u], low[i]);
}
else
low[u] = min(low[u], dfn[i]);
}
}
if (low[u] == dfn[u] && vis[u]){
cnt2++; //双连通分量个数
while (stk.top() != u){
vis[stk.top()] = 0;
belong[stk.top()] = cnt2;
stk.pop();
}
vis[u] = 0;
belong[u] = cnt2;
stk.pop();
}
}
int main(){
while (scanf("%d%d", &n, &m) != EOF)
{
E = deep = 0;
memset(low, 0, sizeof(low));
memset(dfn, 0, sizeof(dfn));
memset(vis, 0, sizeof(vis));
memset(cnt, 0, sizeof(cnt));
memset(belong, 0, sizeof(belong));
while (stk.size())stk.pop();
cnt2 = 0;
for (int i = 1; i <= m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
mp[u][v] = mp[v][u] = 1;
}
dfs(1, 1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (mp[i][j] && belong[i] != belong[j]) //遍历所有边,度++
cnt[belong[i]]++, cnt[belong[j]]++, mp[i][j] = mp[j][i] = 0;
int ans = 0;
for (int i = 1; i <= cnt2; i++)
if (cnt[i] == 1) ans++;
cout << (ans + 1) / 2 << endl;
}
}