[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 }