E18【模板】树上背包 P2014 [CTSC1997] 选课
视频链接:E18【模板】树上背包 P2014 [CTSC1997] 选课_哔哩哔哩_bilibili
题意:有 n 个物品和一个容量是 V 的背包。物品之间具有依赖关系,构成一棵树。如果选择一个物品,则必须选择它的父节点。
思路:在树上做背包DP
- 以每个结点为根的子树看做一个物品组,可能选择:只选根节点,根节点+某些子结点
- f[u][j]:以u为根的子树,体积为j时的最大价值
- dfs在遍历到 u 结点时,符合条件的,先选上根节点 u,即 f[u][v[u] ~ V] = w[u]
- j的范围 [V, v[u]] ,小于v[u]则没有意义,因为连根结点都放不下
- k的范围 [0, j-v[u]],当大于 j-v[u] 时分给儿子树的容量过多,剩余的容量连根节点的物品都放不下了
#include<iostream> #include<cstring> using namespace std; const int N=110; int n,V,p,root; int v[N],w[N]; int h[N],to[N],ne[N],tot; //邻接表 int f[N][N]; void add(int a,int b){ to[++tot]=b;ne[tot]=h[a];h[a]=tot; } void dfs(int u){ for(int i=v[u];i<=V;i++) f[u][i]=w[u]; for(int i=h[u]; i; i=ne[i]){ //子节点 int s=to[i]; dfs(s); for(int j=V;j>=v[u];j--) //体积 for(int k=0;k<=j-v[u];k++)//决策 f[u][j]=max(f[u][j],f[u][j-k]+f[s][k]); } } int main(){ cin>>n>>V; //物品个数,背包容量 for(int i=1;i<=n;i++){ cin>>v[i]>>w[i]>>p; //体积,价值,依赖的物品编号 if(p==-1) root=i; else add(p,i); } dfs(root); cout<<f[root][V]; }
f[u][j]:以 u 点为根的子树,选了 j 门课程的最大学分
递推起点 f[u][1]=w[u]
01背包,逆序枚举 j
f[u][j]=max(f[u][j], f[u][j-k]+f[v][k]), j∈[m+1,1], k∈[0,j-1]
虚拟根节点0:从0号点向没有先修课的课程连边,把森林转化成一颗树
0号点的为必选点,所以共选 m+1门
// 树上背包 O(n^2) #include <iostream> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int N=305; vector<int> e[N]; int n,m,w[N],f[N][N],siz[N]; void dfs(int u){ f[u][1]=w[u];siz[u]=1; for(int v:e[u]){ dfs(v); siz[u]+=siz[v]; for(int j=min(m+1,siz[u]);j;j--) //课程 for(int k=0;k<=min(j-1,siz[v]);k++) //决策 f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]); } } int main(){ scanf("%d%d",&n,&m); for(int i=1,k; i<=n; i++){ scanf("%d%d",&k,&w[i]); e[k].push_back(i); } dfs(0); //虚拟根节点0 printf("%d",f[0][m+1]); }
// 树上背包 O(n^2) #include <iostream> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int N=305; vector<int> e[N]; int n,m,w[N],f[N][N]; void dfs(int x){ for(int y:e[x]){ for(int j=0;j<=m-1;j++) f[y][j]=f[x][j]+w[y]; dfs(y); for(int j=1;j<=m;j++) f[x][j]=max(f[x][j],f[y][j-1]); } } int main(){ scanf("%d%d",&n,&m); for(int i=1,k;i<=n;i++){ scanf("%d%d",&k,&w[i]); e[k].push_back(i); } dfs(0); printf("%d",f[0][m]); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!