您的位置:首页 > 其它

[JZOJ5426]摘Galo

2017-11-05 20:12 183 查看

题目大意:
  有一棵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。

#include<cstdio>
#include<cctype>
#include<vector>
typedef long long int64;
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
const int N=100001;
int par
,w
,size
;
std::vector<std::vector<int64> > f;
std::vector<int64> g;
int main() {
const int n=getint(),k=getint()+1;
for(register int i=2;i<=n;i++) {
par[i]=getint(),w[i]=getint();
}
f.resize(n+1);
g.resize(k+1);
for(register int i=1;i<=n;i++) size[i]=1;
for(register int i=n;i>1;i--) {
size[par[i]]+=size[i];
if(f[i].empty()) f[i].resize(k+1);
if(f[par[i]].empty()) f[par[i]].resize(k+1);
g=f[par[i]];
f[i][1]=std::max(f[i][1],(int64)w[i]);
for(register int j=1;j<=std::min(size[i],k);j++) {
for(register int l=0;l<=std::min(size[par[i]],k)-j;l++) {
g[l+j]=std::max(g[l+j],f[par[i]][l]+f[i][j]);
}
}
f[par[i]]=g;
f[i].clear();
}
int64 ans=0;
for(register int i=0;i<=k;i++) ans=std::max(ans,f[1][i]);
printf("%lld\n",ans);
return 0;
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: