根据题意可以建立一颗树
要想攻占子节点,必须先攻占父亲节点,以这个关系建立森林
在从0号节点引一个关系到所有树根,森林就成为一棵树了
状态定义
opt[i][j]表示以i号节点为根的子树中选择j个点获得的最大价值
状态转移
Gem[i]即输入的b
opt[i][j]=opt[son1][j1]+opt[son2][j2]+....+opt[son-m][jm]+Gem[i]
令d[ii]表示在i号节点的所有子树中选取ii个节点所获得的最大值,通过分组背包计算d数组
则opt[i][j]=d[j-1]+Gem[i]
所以对于opt[i][0...m]可通过求一次分组背包获得的d数组计算得出
代码如下:
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m;
bool flag[220];
int Gem[220];//Gem[i]表示i城堡的宝物数量
vector<int>a[220];//树的存储,城堡的有效编号为1,2,,,n,增加一个不存在0号城堡作为根
int opt[220][220];//opt[i][j]表示i为根的子树中选j个节点获得的最大价值
void dfs(int r)//在以r为根的子树中,计算opt[r][0....m]
{
flag[r]=true;
for(int i=0;i<a[r].size();i++)
{
if(!flag[a[r][i]])
{
dfs(a[r][i]);
}
}
//opt[r][c]=d[c-1]+Gem[i]
//d[i]表示在r的所有子树中选择i个节点获得的最大值
int d[220];
memset(d,0,sizeof(d));
for(int i=0;i<a[r].size();i++)
{
for(int j=m;j>=0;j--)
{
for(int k=0;k<=j;k++)
{
d[j]=max(d[j],d[j-k]+opt[a[r][i]][k]);
}
}
}
opt[r][0]=0;
for(int i=1;i<=m;i++)
{
opt[r][i]=d[i-1]+Gem[r];
}
}
int main()
{
while(scanf("%d%d",&n,&m),n||m)
{
memset(flag,false,sizeof(flag));
for(int i=0;i<=n;i++)
{
a[i].clear();
}
for(int i=1;i<=n;i++)
{
int A;
scanf("%d%d",&A,&Gem[i]);
a[A].push_back(i);
}
Gem[0]=0;
//建树完毕
m++;
dfs(0);
printf("%d\n",opt[0][m]);
}
return 0;
}