Problem
树上背包问题的典例,记下来
solution
设dp[x][t]表示以x为子树,选t门课获得的最大学分
设p是x的子节点数量,ci是x的子节点yi选修的课数
转移方程如下
dp[x][t]=max∑i=1pci=t−1{i=1∑pdp[yi][ci]}+pnt[x]
事实上,这是一个分组背包模型,对于每个节点x,每个子节点yi是一个组,在其中选取不超过1个元素ci加入背包。将当前枚举到的组作为阶段
对于没有先修课的课程,我们可以将一个超级根节点0作为它们的父节点,方便计算
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include <cstdio> #include <algorithm> #include <iostream> #include <cstring> #include <cmath> #include <cstdlib> #include <vector> #define maxn 305 #define maxm 305 using namespace std; typedef long long ll;
int n,m; vector<int> son[maxn]; int prt[maxn]; int pnt[maxn]; int dp[maxn][maxm];
void DP(int u) { for(register int i=0;i<son[u].size();++i) { int v=son[u][i]; DP(v); for(register int t=m;t>=0;--t) for(register int j=t;j>=0;--j) dp[u][t]=max(dp[u][t],dp[u][t-j]+dp[v][j]); } if(u!=0) for(register int t=m;t>0;--t) dp[u][t]=dp[u][t-1]+pnt[u]; }
int main() { scanf("%d%d",&n,&m); int k; for(register int i=1;i<=n;++i) { scanf("%d%d",&k,&pnt[i]); prt[i]=k; } for(register int i=1;i<=n;++i) son[prt[i]].push_back(i); DP(0); printf("%d",dp[0][m]); return 0; }
|