地址: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. xlsx合并单元格简单介绍

    在使用xlsx导出excel表格的时候,有时候我们需要将某些表格进行合并,该如何做呢,代码如下: import XLSX from 'xlsx'; // ... // xlsxData 是 Excel ...

  2. UIPath踩坑记一开发环境检查

      第一步:设置--设计--关闭新项目使用新式体验   第二步:Uipath与浏览器的通信护展是否已安装,如果没有安装需要点击安装   第三步:浏览器中安装的扩展是否已经打开

  3. selenium---xpath定位方法详解

    Xpath定位   验证xpath写的是否正确: 1.打开浏览器检查页面,Ctrl+F,把路径输入进去,如果可以定位到的位置只有一个,说明是对的 2.在需要定位的页面,按F12后,切换至console ...

  4. Python第一次作业(20220909)

    实例01:根据身高.体重计算BMI指数 ①:分别定义两个变量"height"和"weight",用于储存身高(单位:米)和体重(单位:千克).使用内置的prin ...

  5. 关于使用antd-proTable,报错 ResizeObserver loop limit exceeded

    错误如上,原因有几种情况 一:columns中,属性又ellipsis属性,但是没有设置width,导致table不知道如何计算在什么时候,开始对内容进行加省略号,出现了计算错误 如  const c ...

  6. Unity安卓端文件写在外部设置

  7. Nginx + Keepalived 高可用集群部署

    负载均衡技术对于一个网站尤其是大型网站的web服务器集群来说是至关重要的!做好负载均衡架构,可以实现故障转移和高可用环境,避免单点故障,保证网站健康持续运行.在使用 Nginx 做反向代理或者负载均衡 ...

  8. msfconsole的使用

    msfconsole是metasploit中的一个工具: msfconsole集成了很多漏洞的利用的脚本,并且使用起来很简单的网络安全工具 在终端输入msfconsole命令即可进入msf的控制台,m ...

  9. MasaFramework入门第二篇,安装MasaFramework了解各个模板

    安装MasaFramework模板 执行以下命令安装最新Masa的模板 dotnet new --install Masa.Template 安装完成将出现四个模板 Masa Blazor App: ...

  10. 记一次 .NET 某企业 ERP网站系统 崩溃分析

    一:背景 1. 讲故事 前段时间收到了一个朋友的求助,说他的ERP网站系统会出现偶发性崩溃,找了好久也没找到是什么原因,让我帮忙看下,其实崩溃好说,用 procdump 自动抓一个就好,拿到 dump ...