#include using namespace std; const int N = 310; int n, m; int w[N]; int f[N][N]; //创建邻接表 int h[N], e[N], ne[N], idx; void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; } //分组背包过程 void dfs(int u) { for (int i = h[u]; i != -1; i = ne[i]) {//物品组 dfs(e[i]); for (int j = m - 1; j >= 0; j--) //体积 for (int k = 0; k <= j; k++) //决策 f[u][j] = max(f[u][j], f[u][j - k] + f[e[i]][k]); } //把老大加上去,同样由于滚动数组对于自己本行的依赖,所以采用了倒序 //给老大预留了一个位置1,所以i>=1 for (int i = m; i >= 1; i--) f[u][i] = f[u][i - 1] + w[u]; //要注意0这个边界,当当前节点的选课数量为0时是没有意义的,所以需要初始化为0,但是其本身就是0, //所以这里直接不循环即可,类比一下10_2.cpp //f[u][0]=0; } int main() { /** 树形DP+01背包-->映射成分组背包 如果有三个子树,表示有三个物品组,每个物品组中有m+1(0~m)个物品 第k个物品的体积是k,价值:f(s1,k) 第一层:物品组,第二层:从大到小循环体积,第三层:决策 时间复杂度:O(n*m^2) 但是这道题可能不只有一个点没有父结点,而是有好多点都可能没有父结点,这时,我们可以把0看成虚拟的根结点,最终我们取f[0,m+1]即可 同时,因为每选一门课,都需要添加上0这个结点,所以最终是m+1 */ cin >> n >> m; memset(h, -1, sizeof h); for (int i = 1; i <= n; i++) { int p; cin >> p >> w[i]; //添加虚拟根的技巧 add(p, i);//若不存在先修课则该项为0:这为我们创建虚拟根0号结点创造了条件!!! } //所有方案都需要添加虚拟根结点0,就相当于选了m+1门课 m++; dfs(0); cout << f[0][m] << endl;//由于m已经++,所以这里就是f[0][m]了 return 0; }