背包类树形DP 选课题解
我觉得题目给出0节点作为虚拟课程,也避免了我们要去想将若干个森林建成一棵树;将N个节点的森林建成了N+1条边的树;
其次,我们对这个题进行一个分析;
很容易想到F【x,t】表示以x为根的子树中,选择t门课程所获得得最高学分;
在x的子树中选择节点y,再以y为根的子树中,选择c_i门课程,保证Σc_i = t - 1;
初始状态,t=0时,F【x,t】=0;
通过分析状态转移方程,该方程实际上是一个分组背包模型,第i组的第j个物品体积为j,价值为F[y,j],背包总容积t-1;
我们要从每组中选择不超过1个物品(每个子节点y都只能选择一个状态转移到x),在选择总物品不超过t-1的前提下,学分最大;
是背包与树形DP的结合;
#include<bits/stdc++.h> using namespace std; int lin[1000],tot,n,m,x,f[400][400],s[400]; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} x*=f; } struct gg { int y,next; }a[2000]; inline void add(int x,int y) { a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; } inline void dp(int x) { f[x][0]=0; for(int i=lin[x];i;i=a[i].next) { int y=a[i].y; dp(y); //倒序循环当前选课总门数,或者背包的总体积; for(int t=m;t>=0;--t) { //循环更深子树上的选课门数(组内物品); //此处倒序是为了正确处理组内体积为0的物品,这样可以从初始状态f【x,0】转移; for(int j=t;j>=0;--j) { if(t-j>=0) { f[x][t]=max(f[x][t],f[x][t-j]+f[y][j]); } } } } if(x!=0) {//x!=0,选修x本身需要占用1节课,获得相应的学分; for(int t=m;t>0;t--) f[x][t]=f[x][t-1]+s[x]; } } int main() { read(n);read(m); for(int i=1;i<=n;i++) { read(x); add(x,i); read(s[i]); } dp(0); cout<<f[0][m]<<endl; return 0; }