poj 3177 Redundant Paths(边双连通分量+缩点)★

题意:有n个牧场,Bessie 要从一个牧场到另一个牧场,要求至少要有2条独立的路可以走。现已有m条路,求至少要新建多少条路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指:没有公共边的路,但可以经过同一个中间顶点。

分析:在同一个边双连通分量中,任意两点都有至少两条独立路可达,所以同一个边双连通分量里的所有点可以看做同一个点。

缩点后,新图是一棵树,树的边就是原无向图的桥。

现在问题转化为:在树中至少添加多少条边能使图变为双连通图。

结论:添加边数=(树中度为1的节点数+1)/2

具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。

其实求边双连通分量和求强连通分量差不多,每次访问点的时候将其入栈,当low[u]==dfn[u]时就说明找到了一个连通的块,则栈内的所有点都属于同一个边双连通分量,因为无向图要见反向边,所以在求边双连通分量的时候,遇到反向边跳过就行了。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1
#define INF  0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-12
#define maxn 300100
#define MOD 1000000007

const int N = 5000 + 5;
const int M = 10000 + 5;

struct EDGE
{
    int v,next;
}edge[M*2];
int head[N],low[N],dfn[N],belong[N],degree[N],sta[M],instack[M];
int g,cnt,top,scc;
int min(int a,int b)
{
    return a < b ? a : b;
}
void AddEdge(int u,int v)
{
    edge[g].v = v;
    edge[g].next = head[u];
    head[u] = g++;
}
void Tarjan(int u,int fa)
{
    low[u] = dfn[u] = ++cnt;
    sta[++top] = u;
    instack[u] = 1;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].v;
        if(i == (fa^1))
            continue;
        if(!dfn[v])
        {
            Tarjan(v,i);
            low[u] = min(low[u],low[v]);
        }
        else if(instack[v])
            low[u] = min(low[u],dfn[v]);
    }
    if(dfn[u] == low[u])
    {
        scc++;
        while(1)
        {
            int v = sta[top--];
            instack[v] = 0;
            belong[v] = scc;
            if(v == u)
                break;
        }
    }
}

int main()
{
    int n,m,u,v;
    scanf("%d%d",&n,&m);
    g = cnt = top = scc = 0;
    memset(head,-1,sizeof(head));
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    memset(instack,0,sizeof(instack));
    memset(degree,0,sizeof(degree));
    for(int i = 0; i < m; i++)
    {
        scanf("%d%d",&u,&v);
        AddEdge(u,v);
        AddEdge(v,u);
    }
    for(int i = 1; i <= n; i++)
        if(!dfn[i])
            Tarjan(1,-1);
    for(int i = 1; i <= n; i++)
    {
        for(int j = head[i]; j != -1; j = edge[j].next)
        {
            v = edge[j].v;
            if(belong[i] != belong[v])
                degree[belong[i]]++;
        }
    }
    int sum = 0;
    for(int i = 1; i <= n; i++)
        if(degree[i] == 1)
            sum++;
    int ans = (sum+1)/2;
    printf("%d\n",ans);
    return 0;
}




你可能感兴趣的:(思维,图论—强连通分量)