hdu4612 Warm up 树形dp 桥 强连通分量

108 篇文章 0 订阅
32 篇文章 0 订阅

Warm up

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 2530 Accepted Submission(s): 592


Problem Description
  N planets are connected by M bidirectional channels that allow instant transportation. It's always possible to travel between any two planets through these channels.
  If we can isolate some planets from others by breaking only one channel , the channel is called a bridge of the transportation system.
People don't like to be isolated. So they ask what's the minimal number of bridges they can have if they decide to build a new channel.
  Note that there could be more than one channel between two planets.

Input
  The input contains multiple cases.
  Each case starts with two positive integers N and M , indicating the number of planets and the number of channels.
  (2<=N<=200000, 1<=M<=1000000)
  Next M lines each contains two positive integers A and B, indicating a channel between planet A and B in the system. Planets are numbered by 1..N.
  A line with two integers '0' terminates the input.

Output
  For each case, output the minimal number of bridges after building a new channel in a line.

Sample Input
  
  
4 4 1 2 1 3 1 4 2 3 0 0

Sample Output
  
  
0

Source

Recommend
zhuyuanchen520
题目其实就是要求的是加一条边,最终桥最少!这条边加在什么地方呢。如果是在一个连通分量内,加一条边并不能影响最终的结果,所以应该加在缩图后形成树的直径上,所
谓树的直径,也就是树上最长的两条从根到叶子结点的路径,这样就能使得桥的数目减少这个直径的长度,所以最终的桥自然最小了。
那么,我们先用tarjan求强连通分量,就可以得到缩过的图,这样,所有的边都 是桥了,就可以生成一个树了,我们再加一条边使所有的桥要最小,当然,就是加在树的直径上,也就是一个树形dp就可以了,但是这题我们可以在求强连通分量的时候,顺便就把dp求出来了这样,更快,也好理解了!
用dp[i][0] dp[i][1],表示以i为根的最长和第二长的i到叶子结点的路径,dp[i][0] = max(dp[j][0] + i - j 是否为桥?1:0,dp[i][0]);j是i的子结点,如果i - j 是桥,那么就是dp[j][0] + 1的值更新,否则就是dp[j][0](因为没有桥,所以不用加1),dp[i][1],用一样的递推就可以了! 
#pragma comment(linker,"/STACK:102400000,102400000")
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define N 200050
#define M 2000050
int dp[N][2],visit[M],edge_m,head[N],edge[M],next[M],dfn[N],low[N],all,ti;
void addedge(int s,int e)
{
    next[edge_m]=head[s],edge[edge_m]=e,head[s]=edge_m++;
    next[edge_m]=head[e],edge[edge_m]=s,head[e]=edge_m++;
}
void tarjan(int u)
{
    //printf("%d ",u);
    int i,j;
    dfn[u]=low[u]=ti++;//初始化
    dp[u][0]=dp[u][1]=0;
    for(i=head[u];i!=-1;i=next[i])
    {
        j=edge[i];
        if(!visit[i>>1])
        {
            visit[i>>1]=1;
            if(dfn[j]==0)
            {
                tarjan(j);
                all+=(dfn[u]<low[j]);//桥的数目
                int temp=dp[j][0]+(dfn[u]<low[j]);//加一个桥
                if(temp>dp[u][0])//当前子树中的第一大,还是第二大
                {
                     dp[u][1]=dp[u][0];
                     dp[u][0]=temp;

                }
                else if(temp>dp[u][1])
                {
                    dp[u][1]=temp;
                }
                low[u]=min(low[u],low[j]);
            }
            else
            {
                low[u]=min(low[u],dfn[j]);
            }
        }

    }
}
int main()
{
    int n,m,i,s,e,ans;
    while(scanf("%d%d",&n,&m)!=EOF&&m+n)
    {
        memset(head,-1,sizeof(head));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(visit,0,sizeof(visit));
        edge_m=0;all=0;
        for(i=0;i<m;i++)
        {
            scanf("%d%d",&s,&e);
            addedge(s,e);
        }
        ti=1;
        tarjan(1);
        ans=0;
        for(i=1;i<=n;i++)
        {
           ans=max(ans,dp[i][0]+dp[i][1]);//一个子树,第一大和第二大的边就可以组成这个子树的最大直经
        }
        printf("%d\n",all-ans);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值