【边双连通 && 树的直径】HDU - 4612 Warm up

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 200055
struct 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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值