POJ3177 Redundant Paths

来源:互联网 发布:网页源代码加密js 编辑:程序博客网 时间:2024/06/01 23:45

一.  原题链接:http://poj.org/problem?id=3177

二.  题目大意:给一个无向图,要求添加一些边使得任意两点之间最少有2条不同的路径,不同的路径指的是没有公共边的路径。

三.  思路:

1.     我们知道任意一个边双连通分量,满足之中的两两点之间有2条不同的路径,其实利用Tarjan算法求边双连通分量也体现这种结果。假设父亲为u,子女为v,如果low[v]<=dfn[u],说明v可以回到u以及u以上的点,而u到v已经有一条路径了,于是u,v在同一个边双连通分量里面。

2.     于是问题转化成求出多少个边双连通分量,单独一个节点算一个,然后把每个双连通分量看成一个点,也就是缩点,这时候变成了一棵树(无回路),树的叶子节点到叶子节点直接肯定只有一条路径,因为它是叶子节点,要走到另一个叶子节点一定要先返回根,于是我们要做的只需要把叶子节点全部连成一个边双连通图,而n个叶子节点,需要(n+1)/2条边,其实叶子节点就是度为1的边。

3.     于是我们可以用Tarjan求出原图的各个边双连通分量,用并查集把同一连通分量的节点(low[u] <= dfn[v])集合起来,同时求出所有的桥(low[u]>dfn[v]),就是缩点后的树的边。用一个数组blong把所有的边双连通分量标号,然后扫一遍,数出各个双连通分量的度,度为1的个数就是树的叶子节点。然后结果就出来了。

四.代码

#include <iostream>#include <cstdio>#include <queue>#include <cstring>#include <cmath>#include <cstdlib>using namespace std;const int MAX_N = 5008,          INF = 0x3f3f3f3f;class AdjList{public:    struct Edge    {        int v, next;    }edges[MAX_N*MAX_N];    int head[MAX_N], cnt;    void init()    {        cnt = 0;        memset(head, -1, sizeof(head));    }    void addEdge(int u, int v)    {        edges[cnt].v = v,        edges[cnt].next = head[u];        head[u] = cnt++;    }};class FUset{public:    int pre[MAX_N];    void init()    {        memset(pre, -1, sizeof(pre));    }    int Find(int x)    {        int root = x, save;        for(; pre[root] >= 0; root = pre[root]);        for(save = pre[x]; x != root; pre[x] = root, x = save, save = pre[x]);        return root;    }    void Union(int x, int y)    {        int Rootx = Find(x), Rooty = Find(y);        int temp = pre[Rootx] + pre[Rooty];        if(pre[Rootx] > pre[Rooty]){            pre[Rootx] = Rooty;            pre[Rooty] = temp;        }        else{            pre[Rooty] = Rootx;            pre[Rootx] = temp;        }    }};class Tarjan{public:    AdjList graph;    FUset FU;    int bridge[MAX_N][2], low[MAX_N], dfn[MAX_N],        nBridge, nodeNum, belong[MAX_N];//every node belong to which double connection.    bool visited[MAX_N];    void init(int n)    {        nodeNum = n;        nBridge = 0;        graph.init();        FU.init();        memset(visited, 0, sizeof(visited));        memset(belong, -1, sizeof(belong));    }    void dfs(int pre, int u, int depth)    {        visited[u] = true;        low[u] = dfn[u] = depth;        bool toPre = false;        int i, v;        for(i = graph.head[u]; i != -1; i = graph.edges[i].next){            v = graph.edges[i].v;            if(!visited[v]){                dfs(u, v, depth + 1);                low[u] = min(low[u], low[v]);                if(low[v] <= dfn[u])//able to return, belong to the same double connection.                    FU.Union(u, v);                if(low[v] > dfn[u])//unable to return, (u, v) is a bridge.                    bridge[nBridge][0] = u, bridge[nBridge++][1] = v;            }            else if(v != pre || toPre)                low[u] = min(low[u], dfn[v]);            else if(v == pre)                toPre = true;        }    }    int doubleConnection()    {        int i, root, res = 0;        dfs(-1, 1, 1);        for(i = 1; i <= nodeNum; i++){            root = FU.Find(i);            if(belong[root] == -1)                belong[root] = res++;            belong[i] = belong[root];        }        return res;    }    int getRes()    {        int cnt[MAX_N] = {0}, i, num, u, v, res = 0;        num = doubleConnection();        for(i = 0; i < nBridge; i++){            u = bridge[i][0];            v = bridge[i][1];            cnt[belong[u]]++, cnt[belong[v]]++;        }        for(i = 0; i < num; i++)            if(1 == cnt[i])                res++;        return res;    }}G;int main(){    //freopen("in.txt", "r", stdin);    int i, u, v, F, R;    while(~scanf("%d%d", &F, &R)){        G.init(F);        for(i = 0; i < R; i++){            scanf("%d%d", &u, &v);            G.graph.addEdge(u, v);            G.graph.addEdge(v, u);        }        printf("%d\n", (G.getRes()+1)/2);    }    return 0;}

0 0
原创粉丝点击