[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\n",ans);
78 //    for(R i=1;i<=n;i++) printf("%d %d\n",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 }

 

posted @ 2018-05-27 21:44  ww3113306  阅读(576)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。