E18【模板】树上背包 P2014 [CTSC1997] 选课

视频链接:E18【模板】树上背包 P2014 [CTSC1997] 选课_哔哩哔哩_bilibili

题意:有 n 个物品和一个容量是 V 的背包。物品之间具有依赖关系,构成一棵树。如果选择一个物品,则必须选择它的父节点。

思路:在树上做背包DP

  1. 以每个结点为根的子树看做一个物品组,可能选择:只选根节点,根节点+某些子结点
  2. f[u][j]:以u为根的子树,体积为j时的最大价值
  3. dfs在遍历到 u 结点时,符合条件的,先选上根节点 u,即 f[u][v[u] ~ V] = w[u]
  4. j的范围 [V, v[u]] ,小于v[u]则没有意义,因为连根结点都放不下
  5. 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];
}
复制代码

Luogu P2014 [CTSC1997] 选课

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]);
}
复制代码

 

posted @   董晓  阅读(1032)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示