参考资料

背包九讲

一、线性DP

  1. 如果现在在状态 i 下,它上一步可能的状态是什么。
  2. 上一步不同的状态依赖于什么。

根据上面的分析,分析出状态和转移方程。注意:dp 不一定只有两维或者一维,一开始设计状态时先不考虑维度。如果空间超了的话考虑滚动数组等优化,或者再重新设计状态。

可以通过哪一个条件范围小来入手设计状态。

对于边界条件:一般是考虑第一个点的特殊情况,即 \(dp_{[1]}\)。

二、背包问题

1. 01背包

模型总结:每个物品只能选一次。

思想

每件物品可以通过放或者不放来转移,所以它还依赖于之前使用的容量。设 \(dp_{[i][j]}\) 表示前 \(i\) 件物品用了 \(j\) 的容量的最大价值。不难得出:\(dp_{[i][j]}=max(dp_{[i-1][j]},dp_{[i-1][j-v[i]]+w_[i]})\)。

考虑优化维度,常用的方法是优化掉前 \(i\) 个物品的维度(对很多题目也同样适用)。设 \(dp_{[j]}\) 表示目前位置使用 \(j\) 的容量的最大价值。于是状态转移方程变成了:\(dp_{[j]}=max(dp_{[j]},dp_{[j-v[i]]}+w_{[i]})\)。

由于 \(v_{[i]}\ge 0\),所以 \(j-v_{[i]}\le j\),为了保证 \(j-v_{[i]}\) 一定是上一次 \(i-1\) 个物品的,所以我们循环 \(j\) 的时候要倒序循环。或者画出二维 DP 转移列表来理解。

代码:

for(int i=1;i<=n;i++)
for(int j=m;j>=v[i];j--)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);

2. 完全背包

模型总结:每个物品可以选无限次

思想

按照 01 背包的思想设计出初始状态:设 \(dp_{[i][j]}\) 表示前 \(i\) 件物品用了 \(j\) 的容量的最大价值。然后循环枚举选 \(k\) 个物品,仿照上面转移方程求出最大值。

继续考虑优化,依旧设 \(dp_{[j]}\) 表示目前位置使用 \(j\) 的容量的最大价值。但这次不能再循环 \(k\) 个物品了。我们返回去思考为什么 01 背包循环 \(j\) 要倒序循环,因为 01 背包只能选 1 个。那么如果它正序循环的话,\(dp_{[j]}\) 还有可能取到现在正在取的 \(i\) 个物品(因为 \(dp_{j-v[i]}\) 可能在这次循环中已经被更新过了),跟完全背包的模型相符。

代码

for(int i=1;i<=n;i++)
for(int j=v[i];j<=m;j++)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);

3. 多重背包

模型总结:每个物品有各自可选的最多次数 \(k\)。

思想

按照完全背包暴力的思想同样可以得到暴力解法。

时间复杂度超时的原因主要是由于循环了 \(k\) 这个维度,于是接下来需要优化这个维度。这里可以使用二进制优化,将循环枚举的 \(k\) 从 \([1,p]\) (\(p\) 是最多选的个数)变为 \(1,2,4...2^{k-1},p-2^k+1\)(\(k\) 是满足 \(p-2^k+1>0\) 的最大整数)。

现在证明 \([1,p]\) 的所有数都可以被写成上面转化后的数的和:

对于 \(S_1=[0,(2^k-1)]\) 这一区间的数:\(2^x,2^{x-1}\) 中间相差了一个 \(2^{x-1}\),如果 \([1,2^{x-1}-1]\) 中的所有数都可以被写成上面的形式,那么 \([2^{x-1},2^x]\) 中的所有数也可以满足条件,即 \([1,2^x]\) 都会满足条件。于是 \(x\) 的值追根溯源到 \(0\),此时显然 \([1,1]\) 中的所有数都会满足条件,再根据上面的递推就可以证明出来了。

对于 \(S_2=[2^k,p]\) 这一区间的数:\(p=(p-2^k+1)+2^k-1\)。由于这一区间内都是连续的,所以所有数都可以被表示为 \((p-2^k+1)+x\;(x\in S1)\)。证明完毕。

代码

for(int i=1;i<=n;i++){
int maxi;
if(v[i]==0) maxi=p[i];
else maxi=min(p[i],t/v[i]);
for(int k=1;maxi>0;k<<=1){
k=min(k,maxi);
maxi-=k;
for(int j=t;j>=v[i]*k;j--) dp[j]=max(dp[j],dp[j-v[i]*k]+w[i]*k);
}
}

4. 其他

混合背包:只需分类讨论上述情况即可。

二维背包费用(选一个物品有两个价值):只需再多增一维按照上面来转移即可。有时另一种价值的表述方法也会变成“最多选 \(x\) 件物品”。

分组背包(每组里面最多选一件物品):对每组的 \(dp_{[j]}\) 取最大即可。

依赖背包(有些物品必需先选另一种物品后才能选):对于每一个没有依赖的背包分类讨论,只选它自己,选它自己+依赖1……等。

三、区间DP

对于有些一眼能看出来的区间DP题一般都是拆分类/合并类的题,典型的有:P1880 合并石子。但关键要看大区间内的答案能否可以通过小区间内的答案转移而来。

将在一段大区间内进行的DP,分解成小区间内进行的DP,大都需要以下几个条件:

  1. 跨度(阶段)
  2. 起点(状态)
  3. 合并的位置(决策的位置)

比较典型的伪代码:

核心思路:先计算出小区间,再推出大区间,边界一般为区间长为 1 的时候

for 子区间长度 2 to n;
for 起点 1 to 终点不大于n
终点=起点+子区间长度-1
for 决策点 起点 to 终点-1
状态转移方程

四、状压DP

对于有多种情况可以影响答案的时候,通常选择暴力开维度来存储信息。但有时会导致维度太多而爆空间,这时候我们要将形式相同的维度压缩成一维,这就是状压DP

状压DP的一大重点便是位运算,而位运算又会引申出位操作。下面是几种常见位运算及位操作的意义

一个二进制数位 &1 得到它本身。(& 表示两个数的相同位上,只有两个都为 \(1\),最终的值才为 \(1\)。通常被看做两个集合的交集。)

一个二进制数位 ^1 则取反。(^ 表示两个数的相同位上,只有值不同,最终的值才为 \(1\))

一个二进制数位 &0 则赋值为 \(0\)。

一个二进制数位 |1 则赋值为 \(1\)。(| 表示两个数的相同位上,只要有一个为 \(1\),最终值就为 \(1\))

(n>>k)&1 取出二进制下 \(n\) 的第 \(k\) 位 (从右往左)。

n&((1<<k)-1) 取出二进制下 \(n\) 的右 \(k\) 位。

n^(1<<k) 将二进制下 \(n\) 的第 \(k\) 位取反。

n|(1<<k) 将二进制下 \(n\) 的第 \(k\) 位赋值为 \(1\)。

n&(-(1<<k)) 将二进制下 \(n\) 的第 \(k\) 位赋值为 \(0\)。

【算法】基础DP的更多相关文章

  1. 基础DP(初级版)

    本文主要内容为基础DP,内容来源为<算法导论>,总结不易,转载请注明出处. 后续会更新出kuanbin关于基础DP的题目...... 动态规划: 动态规划用于子问题重叠的情况,即不同的子问 ...

  2. 算法之DP

    一般DP 都是有模板的,先初始化,然后找到不同状态下数值的关系,使得某个状态可用另一个状态由一个固定的方式转移而来,列出状态转移方程,这就是DP: 例题 P1216 [USACO1.5]数字三角形 N ...

  3. 2020牛客寒假算法基础集训营2 J题可以回顾回顾

    2020牛客寒假算法基础集训营2 A.做游戏 这是个签到题. #include <cstdio> #include <cstdlib> #include <cstring ...

  4. 2020牛客寒假算法基础集训营1 J题可以回顾回顾

    2020牛客寒假算法基础集训营1 这套题整体来说还是很简单的. A.honoka和格点三角形 这个题目不是很难,不过要考虑周全,面积是1,那么底边的长度可以是1也可以是2, 注意底边1和2会有重复的, ...

  5. Levenberg-Marquardt算法基础知识

    Levenberg-Marquardt算法基础知识 (2013-01-07 16:56:17) 转载▼   什么是最优化?Levenberg-Marquardt算法是最优化算法中的一种.最优化是寻找使 ...

  6. HDU4612(Warm up)2013多校2-图的边双连通问题(Tarjan算法+树形DP)

    /** 题目大意: 给你一个无向连通图,问加上一条边后得到的图的最少的割边数; 算法思想: 图的边双连通Tarjan算法+树形DP; 即通过Tarjan算法对边双连通缩图,构成一棵树,然后用树形DP求 ...

  7. 基础dp

    队友的建议,让我去学一学kuangbin的基础dp,在这里小小的整理总结一下吧. 首先我感觉自己还远远不够称为一个dp选手,一是这些题目还远不够,二是定义状态的经验不足.不过这些题目让我在一定程度上加 ...

  8. 解读Raft(一 算法基础)

    最近工作中讨论到了Raft协议相关的一些问题,正好之前读过多次Raft协议的那paper,所以趁着讨论做一次总结整理. 我会将Raft协议拆成四个部分去总结: 算法基础 选举和日志复制 安全性 节点变 ...

  9. 腾讯2017年暑期实习生编程题【算法基础-字符移位】(C++,Python)

     算法基础-字符移位 时间限制:1秒 空间限制:32768K 题目: 小Q最近遇到了一个难题:把一个字符串的大写字母放到字符串的后面,各个字符的相对位置不变,且不能申请额外的空间. 你能帮帮小Q吗? ...

  10. 算法基础_递归_求杨辉三角第m行第n个数字

    问题描述: 算法基础_递归_求杨辉三角第m行第n个数字(m,n都从0开始) 解题源代码(这里打印出的是杨辉三角某一层的所有数字,没用大数,所以有上限,这里只写基本逻辑,要符合题意的话,把循环去掉就好) ...

随机推荐

  1. Mybatis的使用(4)

    1:解决实体类成员变量和数据库表中字段名称不一致的问题: 方法1:在写sql语句时,给表中的列名起别名,名字和实体类名称一样 方法2:使用resultMap来解决: 例如:实体类中成员变量为id,na ...

  2. 【web自动化测试】Playwright快速入门,5分钟上手

    我喜欢Playwright! 这是微软开源的一款非常强大的自动化工具,再过几年,他很有可能取代Selenium在浏览器自动化的通知地位.使用过一段时间,我没有找到很好的中文资料可以参考,导致很多问题无 ...

  3. 抖音 滑块验证方案 s_v_web_id 参数分析

    本文所有教程及源码.软件仅为技术研究.不涉及计算机信息系统功能的删除.修改.增加.干扰,更不会影响计算机信息系统的正常运行.不得将代码用于非法用途,如侵立删! 抖音web端 s_v_web_id 参数 ...

  4. 结束语句之 break

    C 语言自学之 break Dome1: 找出0-50之间的所有素数,所谓素数就是只能被1和它本身整除的数字,比如:7,13,23等.                运行结果: 2  3  5  7 ...

  5. DevOps落地实践点滴和踩坑记录-(2) -聊聊平台建设

    很久没有写文章记录了,上一篇文章像流水账一样,把所见所闻一个个记录下来.这次专门聊聊DevOps平台的建设吧,有些新的体会和思考,希望给正在做这个事情的同学们一些启发吧. DevOps落地实践点滴和踩 ...

  6. Flutter 检测报错 Unable to locate Android SDK.

    安装好 Flutter SDK 之后,官方建议使用flutter doctor检查 Flutter SDK 的相关配置信息. 如果 Android Studio 安装 Android SDK 的时候选 ...

  7. scp复制发送文件夹到其他服务器上

    简述scp: scp是secure copy的简写,是linux系统下基于ssh登陆进行安全的远程文件拷贝命令. 写法: scp [可选参数] 登录名@地址:源路径  目标路径. 举例:scp -r ...

  8. 详谈 MySQL 8.0 原子 DDL 原理

    柯煜昌 青云科技研发顾问级工程师 目前从事 RadonDB 容器化研发,华中科技大学研究生毕业,有多年的数据库内核开发经验. 文章字数 3800+,阅读时间 15 分钟 背景 MySQL 5.7 的字 ...

  9. Java中如何创建不可变(immutable)类

    什么是不可变类 1. 不可变类是指类的实例一经创建完成,这个实例的内容就不会改变. 2. Java中的String和八个基本类型的包装类(Integer, Short, Byte, Long, Dou ...

  10. Kubernetes 监控--Prometheus 高可用: Thanos

    前面我们已经学习了 Prometheus 的使用,了解了基本的 PromQL 语句以及结合 Grafana 来进行监控图表展示,通过 AlertManager 来进行报警,这些工具结合起来已经可以帮助 ...