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

来源:互联网 发布:paperpass 淘宝 编辑:程序博客网 时间:2024/05/17 21:43

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 41 21 31 42 30 0

Sample Output
0

Source
2013 Multi-University Training Contest 2

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 2000050int 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;}


原创粉丝点击