[JZOJ5426]摘Galo

题目大意:
  有一棵n个结点的树,每个点都有一个权值,你要从中选出不超过k+1个点使得权值和尽量大。
  同时要注意如果一个点被选择,那么它的子树和这个点到根结点路径上的点不能被选择。

思路:
  很水的树形DP。
  f[i][j]表示以i为根的子树中选择了j个点的最大权值和。
  状态转移方程f[par[i]][k]=max{f[par[i]][k]+f[i][j]};
  转移的时候可能会被覆盖掉,因此用一个临时数组g来保存。
  for的时候不能for满,不然就变成O(nk^2)的了,只有60分。
  数组可能会开不下,要用vector。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<vector>
 4 typedef long long int64;
 5 inline int getint() {
 6     register char ch;
 7     while(!isdigit(ch=getchar()));
 8     register int x=ch^'0';
 9     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
10     return x;
11 }
12 const int N=100001;
13 int par[N],w[N],size[N];
14 std::vector<std::vector<int64> > f;
15 std::vector<int64> g;
16 int main() {
17     const int n=getint(),k=getint()+1;
18     for(register int i=2;i<=n;i++) {
19         par[i]=getint(),w[i]=getint();
20     }
21     f.resize(n+1);
22     g.resize(k+1);
23     for(register int i=1;i<=n;i++) size[i]=1;
24     for(register int i=n;i>1;i--) {
25         size[par[i]]+=size[i];
26         if(f[i].empty()) f[i].resize(k+1);
27         if(f[par[i]].empty()) f[par[i]].resize(k+1);
28         g=f[par[i]];
29         f[i][1]=std::max(f[i][1],(int64)w[i]);
30         for(register int j=1;j<=std::min(size[i],k);j++) {
31             for(register int l=0;l<=std::min(size[par[i]],k)-j;l++) {
32                 g[l+j]=std::max(g[l+j],f[par[i]][l]+f[i][j]);
33             }
34         }
35         f[par[i]]=g;
36         f[i].clear();
37     }
38     int64 ans=0;
39     for(register int i=0;i<=k;i++) ans=std::max(ans,f[1][i]);
40     printf("%lld\n",ans);
41     return 0;
42 } 

 

posted @ 2017-11-05 20:12  skylee03  阅读(162)  评论(0编辑  收藏  举报