地址:https://www.cnblogs.com/FReQuenter5156/p/shuxingyilaidp.html/

简介

这类背包本质上是分组背包问题。

将一个节点的每一棵子树看作一组,进行分组背包。所谓分组背包,即在选择物品的时候,一开始将物品分为好几组,在选择时,可以从每一组中至多选择一件物品,问如何获得最大的价值,所以我们每次可以枚举这个组数,用 \(i\) 表示第几组,用 \(j\) 表示体积,用 \(k\) 来表示选择的物品,伪代码如下:

for (int i=0到组数)
for (int j=max_v到0)
for (int k=此组所有物品)
f[j]=max(f[j],f[j-v[k]]+w[k])

注意循环顺序。这个和 01 背包的道理类似。

例题

[CTSC1997] 选课

题面

在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有 \(N\) 门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程 a 是课程 b 的先修课即只有学完了课程 a,才能学习课程 b)。一个学生要从这些课程里选择 \(M\) 门课程学习,问他能获得的最大学分是多少?

题解

定义

\(f_{i,j}\) 表示当前第 \(i\) 个点的子树内选 \(j\) 个点(不包括自己)的最大学分。

转移

直接套板子。初值在输入时已经设置好了,DP 中只要比大小。具体参考代码。

代码

#include<bits/stdc++.h>
#define MAXN 305
using namespace std;
int n,m,k[MAXN],s[MAXN],sz[MAXN],f[MAXN][MAXN];
vector<int> gv[MAXN];
void dfs(int now,int father){
sz[now]=1;
for(auto nx:gv[now]){
if(nx!=father) dfs(nx,now),sz[now]+=sz[nx];
}
}
void dp(int now,int father){
int sum=0;
for(auto nx:gv[now]){
if(nx==father) continue;
sum+=sz[nx];
dp(nx,now);
for(int j=sum;j>=0;j--){
for(int k=0;k<sz[nx];k++){
if(j-k>=1){
f[now][j]=max(f[now][j],f[now][j-k-1]+f[nx][k]);
}
}
}
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>k[i]>>s[i];
gv[k[i]].push_back(i);
f[i][0]=s[i];
}
dfs(0,0);
dp(0,0);
cout<<f[0][m];
}

有线电视网

题面

某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点。

从转播站到转播站以及从转播站到所有用户终端的信号传输费用都是已知的,一场转播的总费用等于传输信号的费用总和。

现在每个用户都准备了一笔费用想观看这场精彩的足球比赛,有线电视网有权决定给哪些用户提供信号而不给哪些用户提供信号。

写一个程序找出一个方案使得有线电视网在不亏本的情况下使观看转播的用户尽可能多。

题解

定义

\(f_{i,j}\) 表示到第 \(i\) 个点供应 \(j\) 个用户的最大收益。最后求答案的时候枚举下 \(f_{1,i}\geq 0\) 的最大 \(i\)。注意初值赋 -INF。

转移

也是套板子。直接看代码应该就能理解。

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define MAXN 3005
using namespace std;
vector<pair<int,int>> gv[MAXN];
int n,m,f[MAXN][MAXN],v[MAXN];
int dp(int now){
if(now>=n-m+1){
f[now][1]=v[now];
return 1;
}
int sum=0;
for(auto nx:gv[now]){
int tmp=dp(nx.fi);
sum+=tmp;
for(int j=sum;j>=1;j--){
for(int k=1;k<=tmp;k++){
if(j-k>=0) f[now][j]=max(f[now][j],f[now][j-k]+f[nx.fi][k]-nx.se);
}
}
}
return sum;
}
signed main(){
memset(f,-0x3f,sizeof(f));
cin>>n>>m;
for(int i=1;i<=n-m;i++){
int k;
cin>>k;
for(int j=1;j<=k;j++){
int a,c;
cin>>a>>c;
gv[i].push_back({a,c});
}
}
for(int i=n-m+1;i<=n;i++) cin>>v[i];
for(int i=1;i<=n;i++) f[i][0]=0;
int maxn=0;
dp(1);
for(int i=0;i<=m;i++) if(f[1][i]>=0) maxn=i;
cout<<maxn;
}

重建道路

题面

一场可怕的地震后,人们用 \(N\) 个牲口棚(编号 \(1\sim N\))重建了农夫 John 的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟一的。因此,牧场运输系统可以被构建成一棵树。

John 想要知道另一次地震会造成多严重的破坏。有些道路一旦被毁坏,就会使一棵含有 \(P\) 个牲口棚的子树和剩余的牲口棚分离,John 想知道这些道路的最小数目。

题解

定义

\(f_{i,j}\) 表示当前第 \(i\) 个点为根的子树保留 \(j\) 个点(包括自己)删去的最小的边数。答案需要枚举所有的子树。

转移

其实也差不多。可以直接看代码。注意初始值 \(f_{i,1}\) 为每个点的出度。这题相对来说比较细节。如方程中的 -1,和自己的“父亲”的连边不能拆。

代码

#include<bits/stdc++.h>
using namespace std;
vector<int> gv[155];
int n,p,ans=0x3f3f3f3f,f[155][155],sz[155],od[155];
bool ir[155];
void dp(int now){
sz[now]=1;
if(!od[now]) return f[now][1]=0,void();
for(auto nx:gv[now]){
dp(nx);
sz[now]+=sz[nx];
for(int j=sz[now];j>=0;j--) for(int k=1;k<j;k++) f[now][j]=min(f[now][j],f[now][j-k]+f[nx][k]-1);
}
}
signed main(){
cin>>n>>p;
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
gv[x].push_back(y),od[x]++,ir[y]=true;
}
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++) f[i][1]=od[i];
int root=0;
for(int i=1;i<=n;i++) if(!ir[i]) root=i;
dp(root);
for(int i=1;i<=n;i++){
if(i==root) ans=min(ans,f[i][p]);
else ans=min(ans,f[i][p]+1);
}
cout<<ans;
return 0;
}

拓展

[JSOI2016]最佳团体

题面

JSOI 信息学代表队一共有 \(N\) 名候选人,这些候选人从 \(1\) 到 \(N\) 编号。方便起见,JYY 的编号是 \(0\) 号。每个候选人都由一位编号比他小的候选人\(R_i\) 推荐。如果 \(R_i = 0\)?,则说明这个候选人是 JYY 自己看上的。

为了保证团队的和谐,JYY 需要保证,如果招募了候选人 \(i\),那么候选人 \(R_i\) 也一定需要在团队中。当然了,JYY 自己总是在团队里的。每一个候选人都有一个战斗值 \(P_i\) ,也有一个招募费用 \(S_i\) 。JYY 希望招募 \(K\) 个候选人(JYY 自己不算),组成一个性价比最高的团队。也就是,这 \(K\) 个被 JYY 选择的候选人的总战斗值与总招募费用的比值最大。

题解

简单说一下。比值类似于 最优比例生成树,可以使用分数规划,然后就可以去套板子了。

没来得及实现,可以参考洛谷题解。

【学习笔记】【题解】树形依赖 DP 选做的更多相关文章

  1. 【学习笔记】动态规划—各种 DP 优化

    [学习笔记]动态规划-各种 DP 优化 [大前言] 个人认为贪心,\(dp\) 是最难的,每次遇到题完全不知道该怎么办,看了题解后又瞬间恍然大悟(TAT).这篇文章也是花了我差不多一个月时间才全部完成 ...

  2. .NET CORE学习笔记系列(2)——依赖注入[7]: .NET Core DI框架[服务注册]

    原文https://www.cnblogs.com/artech/p/net-core-di-07.html 包含服务注册信息的IServiceCollection对象最终被用来创建作为DI容器的IS ...

  3. .NET CORE学习笔记系列(2)——依赖注入[6]: .NET Core DI框架[编程体验]

    原文https://www.cnblogs.com/artech/p/net-core-di-06.html 毫不夸张地说,整个ASP.NET Core框架是建立在一个依赖注入框架之上的,它在应用启动 ...

  4. .NET CORE学习笔记系列(2)——依赖注入[4]: 创建一个简易版的DI框架[上篇]

    原文https://www.cnblogs.com/artech/p/net-core-di-04.html 本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章从 ...

  5. .NET CORE学习笔记系列(2)——依赖注入【3】依赖注入模式

    原文:https://www.cnblogs.com/artech/p/net-core-di-03.html IoC主要体现了这样一种设计思想:通过将一组通用流程的控制权从应用转移到框架中以实现对流 ...

  6. .NET CORE学习笔记系列(2)——依赖注入【2】基于IoC的设计模式

    原文:https://www.cnblogs.com/artech/p/net-core-di-02.html 正如我们在<控制反转>提到过的,很多人将IoC理解为一种“面向对象的设计模式 ...

  7. 「学习笔记」wqs二分/dp凸优化

    [学习笔记]wqs二分/DP凸优化 从一个经典问题谈起: 有一个长度为 \(n\) 的序列 \(a\),要求找出恰好 \(k\) 个不相交的连续子序列,使得这 \(k\) 个序列的和最大 \(1 \l ...

  8. .NET CORE学习笔记系列(2)——依赖注入[5]: 创建一个简易版的DI框架[下篇]

    为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在上篇中我们介绍了Cat的基本编程模式,接下来我们就来聊聊Cat的 ...

  9. .NET CORE学习笔记系列(2)——依赖注入【1】控制反转IOC

    原文:https://www.cnblogs.com/artech/p/net-core-di-01.html 一.流程控制的反转 IoC的全名Inverse of Control,翻译成中文就是“控 ...

  10. 汇编入门学习笔记 (七)—— dp,div,dup

    疯狂的暑假学习之  汇编入门学习笔记 (七)--  dp.div.dup 參考: <汇编语言> 王爽 第8章 1. bx.si.di.和 bp 8086CPU仅仅有4个寄存器能够用 &qu ...

随机推荐

  1. shrding-jdbc分表引起的坑

    1.sum等函数不能解析,报错 2.3.1版本,分页,计算出错,第二页以后数据出现问题 3.4.1版本,创建索引添加"`"关键字报错,因为会给索引名拼接上表名,组装后的sql错误.

  2. 配置git到码云

    一.安装完git之后,对项目文件点击右键选择Git Base Here #Git 全局设置用户名与邮箱 git config --global user.name "" git c ...

  3. 关于Electron环境配置与一些相关的错误解决

    安装步骤: 1.安装NVM: 这个是nodejs的版本管理器,github上有一个nvm for windows,由于不能的nodejs的版本问题,可以自由进行切换. 下载地址:https://git ...

  4. 解决定时任务crontab运行待top的脚本

    脚本编写如: #!/bin/bash /usr/bin/top -n -1 -u mysql > /root/script/log/$(date +"%Y%m%d_%H%M%S&quo ...

  5. vue 数组修改 页面无法刷新

    saveData: { current: 1, records:[] , total:0}, countSaveMoney:{ bidSuccessMoney:0, saveMoney:0},页面上有 ...

  6. mongodb展开数组数据

    核心是$unwind操作 db.getCollection("orders").aggregate([{$unwind:"$OrderTrackingDetails&qu ...

  7. C++编码注意事项

    1. vector,string不能按位赋值(vector用push_back, string用重载"+"号)

  8. 树状分级框架UI实例

    树状分级框架UI实例:(内容参考https://zhuanlan.zhihu.com/p/108485875) #coding:utf8 #!/usr/bin/env python #@author: ...

  9. 基于 Agora SDK 实现 Windows 端的多人视频互动(基于3.6.2版本)

    本文介绍如何通过 Agora SDK 在 Windows 平台快速实现互动直播.互动直播和实时通话的区别就在于,直播频道的用户有角色之分.你可以将角色设置为主播或者观众,其中主播可以收.发流,观众只能 ...

  10. University of Toronto Faculty of Arts and Science MAT344– Final Assessment Combinatorics Instructors: Stanislav Balchev and Max Klambauer 19 August 2020

    目录 随便找的一份测试题 T7 T9 T6 T5 solution to (a) solution to (b) solution to (c) solution to (d) T1 T2 T3 T4 ...