hdu4612 Warm up

公开20190417远古博客,汗 

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4612

题意:

给n个点m条无向边,保证整体是个连通图,问加一条边之后保留的桥数量最小值

思路:

无向图找桥,tarjan;之后缩点;再之后找到缩点之后由桥当作树边的树的直径,桥数-直径(最长路径长度)即为所求

坑:

1. 没有缩点,直接接拿桥做,这样生成的将会是森林,而且森林中的树也都不是我要的树

2. tot = tc = 0,因为建双向,保证双向可以通过i,i^1完成对一对互为反向边的边的操作,要保证01,23,45。。。

所以可以A. tot = 0,tot ++

              B. tot = 1,++ tot

3. 初始化的数组看少了,dfn和c一开始没有初始化,因为看到有赋值以为不用,但是忽略了是有条件的赋值,如果原值 = 0,才会赋值。所以要初始化为零,还有要注意记录数组下标的变量的赋初值,初始化标记数组,还有上述的数组。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
const int maxm = 2e6 + 5;
int head[maxn],ver[maxm],Next[maxm];
int dfn[maxn],low[maxn],n,m,tot,num;
bool bridge[maxm];

void add(int x,int y)
{
    ver[++tot] = y;
    Next[tot] = head[x];
    head[x] = tot;///
}

void tarjan(int x,int in_edge)
{
    dfn[x] = low[x] = ++num;
    for (int i = head[x];i;i = Next[i])
    {
        int y = ver[i];
        if(!dfn[y])
        {
            tarjan(y,i);
            low[x] = min(low[x],low[y]);

            if(low[y] > dfn[x])
                bridge[i] = bridge[i ^ 1] = 1;
        }
        else if(i != (in_edge ^ 1))
            low[x] = min(low[x],dfn[y]);
    }
}

int c[maxn],dcc;
void dfs(int x){
    c[x] = dcc;
    for (int i = head[x] ;i;i = Next[i])
    {
        int y = ver[i];
        if(c[y] || bridge[i])
            continue;
        dfs(y);
    }
}

int hc[maxn],vc[maxm],nc[maxm],tc;
void add_c(int x,int y)
{
    vc[++tc] = y;
    nc[tc] = hc[x];
    hc[x] = tc;
}

int v[maxn],d[maxn],ans;
void dp(int x)
{
    v[x] = 1;
    for (int i = hc[x];i;i = nc[i])
    {
        int y = vc[i];
        if(v[y])continue;
        dp(y);
        ans = max(ans,d[x]+d[y]+1);
        d[x] = max(d[x],d[y] + 1);
    }
}

void init()
{
    #define memset(a,b) memset(a,b,sizeof(a))
    memset(head,0);
    memset(hc,0);
    memset(d,0);
    memset(c,0);
    memset(v,0);
    memset(dfn,0);
    memset(bridge,0);
    tc = tot = 1;
    dcc = num = ans = 0;
}

int main()
{
    while(scanf("%d%d",&n,&m) &&(n + m))
    {
        init();
        int x,y;
        for (int i = 1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        for (int i = 1;i<=n;i++)
        {
            if(!dfn[i])
                tarjan(i,0);
        }
        for (int i = 1;i<=n;i++)
        {
            if(!c[i])
            {
                ++dcc;
                dfs(i);
            }
        }
        for (int i = 2;i<=tot;i++)
        {
            int x = ver[i ^ 1],y = ver[i];
            if(c[x] == c[y])continue;
            add_c(c[x],c[y]);
        }
        dp(1);
        int sum = 0;
        for (int i = 2;i < tot;i += 2)
        {
            if(bridge[i])
                sum ++;
        }
        sum -= ans;
        cout<<sum<<endl;
    }

}
/*
7 7
1 2
1 3
1 4
1 5
2 3
2 6
3 7*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值