ps:动态规划章节 - 背包问题
文章目录
动态规划!
很重要的五部曲!
斐波那契数
这道题主要是用来加深理解dp做题步骤的
爬楼梯
使用最小花费爬楼梯
和前面几题一样,可以优化空间复杂度
不同路径
当前状态和前面的状态有关直接dp
一开始想的是BFS不过太久没写了 就没试,DFS会超时
不同路径II
就是多了有障碍物的情况
整数拆分
是一道不太好想的题
关键是要明确我们定义的dp是啥含义
n较大的数可以拆成 较小n * 某个数 得来的,所以我们可以使用dp
思路: 拆数字,要至少拆成两份,然后里面还可以继续拆
优化后的
01背包理论基础
只需掌握01背包和完全背包就可以了
01背包暴力解法
二维数组解法
对于背包问题,有一种写法, 是使用二维数组,即dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
递推公式
初始化
遍历顺序
有两个遍历的维度:物品与背包重量
那么问题来了,先遍历 物品还是先遍历背包重量呢?
其实都可以!! 但是先遍历物品更好理解。
01背包典例
递推公式还是很巧的
#include<bits/stdc++.h>
using namespace std;
// dp[i][j] 表示从下标0到i的物品里面任意取,放进容量为j的背包里,最大的val为dp[i][j]
int val[5005], spa[5005], dp[5005][5005];
int main()
{
int m,n;
cin>>m>>n;
for(int i=0;i<m;i++) cin>>spa[i];
for(int i=0;i<m;i++) cin>>val[i];
//初始化
for(int i=spa[0];i<=n;i++) dp[0][i] = val[0];
for(int i=1; i<m; i++){ //从没有初始化的地方遍历材料
for(int j=0 ; j<=n ;j++){ //遍历容量
if(j < spa[i]) dp[i][j] = dp[i-1][j]; //不能装下时
else dp[i][j] = max(dp[i-1][j], dp[i-1][j-spa[i]]+val[i]); //可以装下时,有两种选择
}
}
cout<<dp[m-1][n];
return 0;
}
01背包理论基础(滚动数组)
步骤
- 关键是遍历容量的时候是倒序的
- 还有遍历的边界条件
为什么要倒序?
简单理解就是正序遍历会重复统计(状态重复),倒序遍历不会
01背包典例
#include<bits/stdc++.h>
using namespace std;
int dp[5005], spa[5005], val[5005]; //dp[j] 表示容量为j的背包里最大能装入的价值为dp[j]
int main()
{
int m,n;
cin>>m>>n;
for(int i=0;i<m;i++) cin>>spa[i];
for(int i=0;i<m;i++) cin>>val[i];
//默认初始化为0
for(int i=0;i<m;i++){ //遍历物品
for(int j=n;j>=spa[i];j--){ //倒序遍历 容量
dp[j] = max(dp[j], dp[j-spa[i]]+val[i]); //max(不放i, 放i)
}
}
cout<<dp[m];
return 0;
}
分割等和子集
如何把它抽象成01背包问题的,我觉得还是挺巧的
再次搞清楚什么是01背包
有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
关键点是总和最大
为什么本题可以用01背包做
- 背包的体积为sum / 2
- 背包要放入的商品**(集合里的元素)重量为 元素的数值,价值也为元素的数值**
- 背包如果正好装满,说明找到了总和为 sum / 2 的子集。
- 背包中每一个元素是不可重复放入。
我们要求目标值,我们就把它抽象成一个背包,如果这个目标值背包可以被装满,说明我们可以取得这个目标值
最后一块石头的重量II
很难想象它是一个01背包,我是看了老师的提示(分成两堆)才做出来的
本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了。
[外链图片转存中…(img-F4GLwhWh-1713150382140)]
小总结
二维01背包
虽然老师后面用的都是一维的,但是二维的其实也挺简单好记的
一维01背包
后面的两道题都是等分子集的问题, 遇到需要等分子集的时候可以想想能不能01背包吧
不对,上面的那两道题其本质还是求背包最多能装多少
目标和
这道题是可以用DFS的,下次复习的时候,拿DFS做一遍
本题则是装满有几种方法。其实这就是一个组合问题了。
数学问题
分成两堆,正好两个方程,解方程
装满背包有几种方法 – 递推公式
目前遇到两种题型
-
背包最多能装多少val – 所以有个关键词是最多 ,双指针有个关键词是最少 -
也有题型是求装满的时候,最少装多少val. 所以最多最少也都可以考虑dp . 双指针有个关键词是最少
-
装满背包有几种方法
感觉dp的题还是得多做,把一些基础的题型都过了
一和零
虽然第一次看见这种题感觉不好想,但不可否认它是一道好题
两个维度(0 和 1,然后抽象成两种重量) , 从一维延伸成二维真的很妙
最多有多少个子集,转换为背包里面最多可以放几个子集
在对比一下一维的
完全背包理论基础
有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。
完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。
01背包和完全背包唯一不同就是体现在遍历顺序上
完全背包例题
为什么要正序遍历
零钱兑换II
是一道很经典的题,也引出了一个概念,组合数&排序数
[外链图片转存中…(img-9IquzZkV-1713150382143)]
小结论
b站一些优质评论– 便于理解
小总结
求有几种方法时
二维01背包
完全背包
组合总和IV
注意一下先遍历背包的一些细节
-
j 的初始化
-
有 if 条件
先遍历容量的时候,j从0或者从1开始都可以
爬楼梯(加强版)
求排列数的完全背包
零钱兑换
一开始没a出来,后面看题解后发现是初始化那块不太理解,有问题。
动规五部曲,做不出的可以打印dp数组看看,自己排排错。
最小的硬币个数 = 最小的背包val
最小的背包val关键点
- 初始化
- 递推公式
完全平方数
直接秒了哈哈哈
单词拆分
很少做这种题,还是挺不好想的
难点:
- 这里是完全背包的 排列数 问题
- dp[] 数组的定义不好想
- for循环那里 ,i 是长度,j 是下标。 求对应关系的时候得动手模拟
背包问题大总结
此文章用于笔者记录学习,也希望对你有帮助