首先推荐国家集训队论文一篇:《浅谈几类背包问题-徐持衡》
http://wenku.baidu.com/view/751dd3ee856a561252d36f44.html
质量很高的一道题,练习赛的时候没做出来,写成了二维的记忆化搜索,150000的数据量,铁定超时。
正确解法应该每个节点开一个一维临时背包,再有一个全局背包记录当前状态(子树)最优值,通过局部背包去优化当前状态下全局背包的值,这样能使状态从二维降到一维,另外,倒着枚举背包的体积可以做到无后效性,整体用记忆化搜索实现。
代码如下:
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
vector<int>son[150005];
int dp[305],K,N,w[150005];
const int max_int=10000000;
int dfs(int u)
{
int t=0,cur[305];
for (int i=1; i<=K; ++i)
dp[i]=cur[i]=-max_int;
dp[0]=cur[0]=0;
int size=son[u].size();
for (int i=0; i<size; ++i)
{
int now=dfs(son[u][i]);
for (int j=t; j>=0; --j)
for (int k=1; j+k<=K && k<=now; ++k)
cur[j+k]=max(cur[j+k],cur[j]+dp[k]);
t+=now;
}
if (!size)
t=1;
cur[1]=max(cur[1],w[u]);
for (int i=0; i<=K; ++i)
dp[i]=cur[i];
return t;
}
int main()
{
int i,j,root;
while (scanf("%d%d",&N,&K)!=EOF)
{
for (i=0; i<=N; ++i)
son[i].clear();
int r;
for (i=1; i<=N; ++i)
{
scanf("%d%d",&r,&w[i]);
if (!r)
root=i;
else
son[r].push_back(i);
}
dfs(root);
if (dp[K]==-max_int)
puts("impossible");
else
printf("%d\n",dp[K]);
}
return 0;
}