思路
树形背包的模板题。加一个虚拟的0结点然后选m+1个科目就OK。
sz[u]sz[u]sz[u]表示以uuu为根的这棵子树的节点数目
dp[u][j]dp[u][j]dp[u][j]表示uuu以uuu为根节点的子树,选取jjj门课且必须选自己的最优解。
dp[u][j]=max(dp[u][j−k]+dp[v][k]),(j:min(m,sz[u])−>2)(k:1−>min(j−1,sz[v])dp[u][j] = max(dp[u][j - k] + dp[v][k]),(j:min(m,sz[u])->2)(k:1->min(j-1,sz[v])dp[u][j]=max(dp[u][j−k]+dp[v][k]),(j:min(m,sz[u])−>2)(k:1−>min(j−1,sz[v])
其实树形背包的第一维就是表示一个结点信息,第二维就是01背包中的容量。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 305;
int n, m, s[MAXN], sz[MAXN], dp[MAXN][MAXN];
vector<int> G[MAXN];
void dfs(int u, int fa)
{
sz[u] = 1, dp[u][1] = s[u];
for (auto v : G[u])
{
if (v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
for (int j = min(m, sz[u]); j >= 2; j--)
for (int k = 1; k <= min(j - 1, sz[v]); k++)
dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[v][k]);
}
}
int main()
{
scanf("%d%d", &n, &m);
m++;
for (int i = 1; i <= n; i++)
{
int k; scanf("%d%d", &k, &s[i]);
G[k].push_back(i);
G[i].push_back(k);
}
dfs(0, -1);
printf("%d\n", dp[0][m]);
return 0;
}
/*
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
*/