zoukankan      html  css  js  c++  java
  • [HAOI2009]毛毛虫 树形DP

    题意:

    给你一棵树,从树中取出一部分满足:是一条链+一些直接连在这条链上的节点

    求节点数最多的合法取出部分。

    题解:

    其实这题还是不难?

    观察到对于任意一条链,

    只有两种情况: 一条路走到底 or  以某个点为中转

    f[x]表示从x往下走,一路走到底的包括x的最优解,

    f[x]包括x也包括father[x](将会加入它的贡献)

    观察到以某个点为中转的情况:

    倘若某条链以一个点为中转,那么这条链将无法向上产生贡献,

    若没有,则变为第一种情况,且一定可以向上产生贡献, 以点x为中转的所有链都可以通过各个儿子的搭配得到

    因此f[x]可以直接从f[son]中选取最优的来得到,

    然后用f[x]来更新ans,

    再选取儿子中的前2大,搭配起来加上x组成链,更新ans

    所以dfs一遍然后输出ans即可,复杂度O(n);

    细节还是挺多的,要注意。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int
     4 #define AC 350000
     5 #define ACway 700000
     6 #define getchar() *o++
     7 char READ[10001000],*o=READ;
     8 int n,m,ans;
     9 int in[AC],f[AC];
    10 int date[ACway],Head[AC],Next[ACway],tot;
    11 /*观察到对于任意一条链,只有两种情况:
    12 一条路走到底 :以某个点为中转
    13 f[i]表示从i往下走,一条路走到底的最优解(不包括i)(非最长链)
    14 但这样并不方便。。。。因为要分的情况太多,
    15 所以f[x]表示从x往下走,一路走到底的包括x的最优解,
    16 f[x]包括x也包括father[x]
    17 观察到以某个点为中转的情况:
    18 倘若某条链以一个点为中转,那么这条链将无法向上产生贡献,
    19 若没有,则变为第一种情况。且一定可以向上产生贡献,
    20 以点x为中转的所有链都可以通过各个儿子的搭配得到,*/
    21 
    22 inline int read()
    23 {
    24     int x=0;char c=getchar();
    25     while(c > '9' || c < '0') c=getchar();
    26     while(c >= '0' && c <= '9') x=x*10+c-'0',c=getchar();
    27     return x;
    28 }
    29 
    30 inline void upmax(int &a,int b)
    31 {
    32     if(b > a) a=b;
    33 }
    34 
    35 inline void add(int f,int w)
    36 {
    37     date[++tot]=w,Next[tot]=Head[f],Head[f]=tot;
    38     date[++tot]=f,Next[tot]=Head[w],Head[w]=tot;
    39     ++in[f],++in[w];
    40 }
    41 
    42 void pre()
    43 {
    44     int a,b;
    45     n=read(),m=read();
    46     for(R i=1;i<=m;i++) 
    47     {
    48         a=read(),b=read();
    49         add(a,b);
    50     }
    51 }
    52 
    53 void dfs(int x,int fa)
    54 {
    55     int now,maxn=0,maxn2=0;
    56     f[x]=1 + in[x];//因为包括了自己,所以至少也是1 + in[x]了
    57     for(R i=Head[x] ; i ;i=Next[i])
    58     {
    59         now=date[i];
    60         if(now == fa) continue;
    61         dfs(now,x);
    62         upmax(f[x],f[now] + in[x] - 1);//因为既要加自己,又要减儿子,抵消了,所以只用加in就可以了
    63         if(f[now] > maxn)//但是由于f[now]会包括x,所以也要减掉,,,
    64         {
    65             maxn2=maxn;
    66             maxn=f[now];
    67         }
    68         else upmax(maxn2,f[now]);
    69     }
    70     upmax(ans,f[x]);
    71     upmax(ans,maxn + maxn2 + in[x] - 3);//同上,只不过多减一个儿子
    72 }//因为也会包括x,所以会重复2次
    73 
    74 void work()
    75 {
    76     dfs(1,0);//随便选个点做根节点吧
    77     printf("%d
    ",ans);
    78 //    for(R i=1;i<=n;i++) printf("%d %d
    ",i,f[i]);
    79 }
    80 
    81 int main()
    82 {
    83 //    freopen("in.in","r",stdin);
    84     fread(READ,1,10000000,stdin);
    85     pre();
    86     work();
    87 //    fclose(stdin);
    88     return 0;
    89 }
  • 相关阅读:
    三方协议,档案,工龄,保险,户口,
    老爸-军事
    反思,关于问 问题,
    计算机组成原理,
    c语言中定义函数和调用函数(在函数中调用其他函数,计算四个数中的最大值)
    c语言中定义函数和调用函数(计算三个数中的最大值)
    c语言中函数的定义和调用(计算1到n之间的所有整数的和)
    c语言中函数的定义和调用(值传递,计算x的n次幂)
    c语言中定义函数和调用函数(将函数的返回值作为参数传递给其他函数,计算平方差)
    c语言中定义函数和调用函数(在函数中调用其他函数,计算int型整数的4次幂)
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9097545.html
Copyright © 2011-2022 走看看