【边双连通 && 树的直径】HDU

来源:互联网 发布:百度云管家mac版 编辑:程序博客网 时间:2024/05/16 15:09

Problem Description

输入n,m代表有n个点,有m条边。有重边,这里重边指的是1-2,1-2如果出现了两次,代表1-2有两条不同的道路接下面m行,每行u,v代表u-v有一条道路(双向)。问你加一条边,最少还剩下多少个桥

思路:

树的直径参考:http://blog.csdn.net/Triple_WDF/article/details/50118115 将图按双连通分量缩点后,桥的数量-直径的长度 就是结果。
有重边所以得特殊处理下,1-2,2-1如果是同一条边,那么1到2后 2不能到1。如果不是同一条边1-2后,2可以到1。所以求双连通分量的时候判断一下是不是同一条边就可以了

#include<bits/stdc++.h>using namespace std;#define mm 1000055#define nn 200055struct node{    int to, next;};node Map[2 * mm];//前向星存图int head[nn], low[nn], dfn[nn], vis[nn], top, sig, N, Stack[nn];//vis[i]代表i属于vis[i]这个双连通分量int n, vv[nn], d, flag;node MAP[mm];int cc;void add(int u, int v, int &cnt)//相邻的cnt奇偶是同一条边,例如0,1是同一条边 奇数^1 为偶数(奇数-1) 偶数^1 为 奇数(偶数+1).所以相当于同一条边{    Map[cnt].to = v;    Map[cnt].next = head[u];    head[u] = cnt++;}void tardfs(int u, int father){    low[u] = dfn[u] = sig++;    Stack[top++] = u;    for(int i = head[u]; ~i; i = Map[i].next)    {        int to = Map[i].to;        if(!dfn[to])        {            tardfs(to, i);            low[u] = min(low[u], low[to]);            if(low[to] > dfn[u])//记录割边两端的的点            {                MAP[cc].to = u;                MAP[cc++].next = to;            }        }        else if(!vis[to] && i != (father ^ 1))//不是同一条边        {            low[u] = min(low[u], dfn[to]);        }    }    if(low[u] == dfn[u])//缩点    {        N++;        do        {            int t = Stack[top - 1];            vis[t] = N;            top--;        }while(Stack[top] != u);    }}void tarjan(){    top = 0, sig = 1, N = 0;    memset(vis, 0, sizeof(vis));//初始化    memset(dfn, 0, sizeof(dfn));    for(int i = 1; i <= n; i++)    {        if(!vis[i]) tardfs(i, i);    }}void dfs(int u){    for(int i = head[u]; ~i; i = Map[i].next)    {        int to = Map[i].to;        if(!vv[to]) {            vv[to] = vv[u] + 1;            if(vv[to] > d)            {                d = vv[to];                flag = to;            }            dfs(to);        }    }}int main(){    int m, u, v;    while(~scanf("%d %d", &n, &m))    {        if(!n && !m) break;        int cnt = 0;//初始化        memset(head, -1, sizeof(head));        while(m--)        {            scanf("%d %d", &u, &v);            add(u, v, cnt);//双向            add(v, u, cnt);        }        cc = 0;        tarjan();//缩点,求双连通分量        cnt = 0;        memset(head, -1, sizeof(head));        for(int i = 0; i < cc; i++)//按照缩点后,建图        {            u = MAP[i].to, v = MAP[i].next;            if(vis[u] != vis[v]) {            add(vis[u], vis[v], cnt);            add(vis[v], vis[u], cnt);            }        }        memset(vv, 0, sizeof(vv));//两次dfs求树的直径        vv[1] = 1;        d = 1;        flag = 1;        dfs(1);        memset(vv, 0, sizeof(vv));        vv[flag] = 1;        d = 1;        dfs(flag);        printf("%d\n", cc - d + 1);    }}
原创粉丝点击