POJ 3177(无向图缩点)

题目描述:

有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;
    }
}

猜你喜欢

转载自blog.csdn.net/kzn2683331518/article/details/81139956