poj 3352 Road Construction(边连通+tarjan+缩点)

http://poj.org/problem?id=3352


题意:简化一下原题题意,意思就是给定一个连通图,问至少要加入几条边使得整个图变成一个边连通图,即图中任意两点都有两条以上的路径(不一定直接相连)。


思路:tarjan算法,设置一个low数组,在建立深搜树的过程中,我们会得到每个节点的low值,对于low值相等的节点在同一个双连通分量中。由于在同一个边连通分量中的点的“地位”是相同的,因此可以将这些双连通分量缩点,就形成一颗无根树。

要让这个无根树变成一个双连通分量,我们需要计算无根树种度为1的节点数平p, 那么答案就是(p+1)/2;

 poj3177与这个题类似,不过这个题中没有重边,在3177中,只需加上判重边就可以了。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
#include <vector>
using namespace std;
const int maxn = 5005;
bool map[maxn][maxn];
vector <int> edge[maxn];
int n,m;
int dfn[maxn],low[maxn],instack[maxn],dep;

void tarjan(int u, int fa)
{
    dfn[u] = low[u] = ++dep;
    instack[u] = 1;

    for(int i = 0; i < (int)edge[u].size(); i++)
    {
        int v = edge[u][i];
        if(v == fa) 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]);
    }
}

int main()
{
	scanf("%d %d",&n,&m);
    memset(map,false,sizeof(map));
    for(int i = 0; i <= n; i++)
        edge[i].clear();
    int u,v;
    for(int i = 0; i < m; i++)
    {
        scanf("%d %d",&u,&v);
        if(!map[u][v])
        {
            edge[u].push_back(v);
            edge[v].push_back(u);
            map[u][v] = map[v][u] = 1;
        }
    }

    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(instack,0,sizeof(instack));
    dep = 0;
    tarjan(1,-1);

    int leaf[maxn];
    memset(leaf,0,sizeof(leaf));

    for(int u = 1; u <= n; u++)
    {
        for(int i = 0; i < (int)edge[u].size(); i++)
        {
            int v = edge[u][i];
            if(low[u] != low[v])
                leaf[ low[u] ] ++;
        }
    }

    int cnt = 0;
    for(int i = 1; i <= n; i++)
    {
        if(leaf[i] ==1)
            cnt++;
    }

    printf("%d\n",(cnt+1)/2);

    return 0;
}



你可能感兴趣的:(Tarjan,双连通分量,缩点)