lc没有背包问题,只有水壶跟鲜花的问题。就来acwing讨生活啦。
背包九讲入门款:
首先呢,根据y神讲的顺序,俺也先来给一下:
题目:
有 NN 件物品和一个容量是 VV 的背包。每件物品只能使用一次。
第 ii 件物品的体积是 vivi,价值是 wiwi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,VN,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 NN 行,每行两个整数 vi,wivi,wi,用空格隔开,分别表示第 ii 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤10000<N,V≤1000
0<vi,wi≤10000<vi,wi≤1000
输入样例
4 5 1 2 2 4 3 4 4 5
输出样例:
8
解析(朴素解法\二维空间解法)
先大概说一下原理,背包问题的朴素解法的子问题就是,我们关心的是第i件物品是否放入背包的问题。我们只有选和不选两种选择:
不选 就是 dp[i][j]=dp[i-1][j],也就是在空间大小为j的条件下,不加第i个物品的情况。 选 就是dp[i][j] = dp[i-1][j-v[i]]+w[i],也就是这个位置放的是不算物品i的占用空间v[i]的时候,那个空间所产生的价值加上i的价值w[i]的价值。
选和不选取最大值就好啦。 such as:
**这里参考的是《算法图解》
手机:重1磅,价值1500元
吉他:重1磅,价值1500元
笔记本电脑:重3磅,价值4000元
现在有一个最多装4磅的袋子看看怎么装最大。
| 1磅 | 2磅 | 3磅 | 4磅 | |
|---|---|---|---|---|
| 手机 | ||||
| 吉他 | ||||
| 笔记本电脑 |
第一行只考虑手机,所以我们能拿到的最高价值就是1500所以全填1500。
| 1磅 | 2磅 | 3磅 | 4磅 | |
|---|---|---|---|---|
| 手机 | 1500 | 1500 | 1500 | 1500 |
| 吉他 | ||||
| 笔记本电脑 |
第二行有吉他了,我们考虑两种情况,不选吉他,看同列上面一行的价值,吉他行1磅列,那么不选吉他在一磅的情况下价值是1500,选吉他,看1磅-1磅这表格里没有这个,写代码的时候,咱们是从1开始的,这些是代码的问题,就不要深究了,0+1500 = 1500,所以吉他一磅这里最高的价值是1500,然后看吉他行2磅列,不选吉他在一磅的情况下价值是1500,2磅-1磅是1磅,故看上一行,一磅列的值,加上吉他的价值,也就是3000,就是价值了。剩下的同理。也就是:
| 1磅 | 2磅 | 3磅 | 4磅 | |
|---|---|---|---|---|
| 手机 | 1500 | 1500 | 1500 | 1500 |
| 吉他 | 1500 | 3000 | 3000 | 3000 |
| 笔记本电脑 |
最后一行,也就是笔记本电脑行,因为笔记本电脑为3磅,所以1磅,2磅的值都为不选笔记本电脑的值,也就是1500和3000,看第三列,不选笔记本电脑为3000,如果选了,那么看3磅-3磅 = 0,所以此时价值为0,此时的价值为4000+0,也就是4000,4磅列的话,同理不选笔记本电脑为3000,如果选了,那么看4磅-3磅 = 1磅,上一行1磅的价值的为1500,加上4000,也就是5500。
| 1磅 | 2磅 | 3磅 | 4磅 | |
|---|---|---|---|---|
| 手机 | 1500 | 1500 | 1500 | 1500 |
| 吉他 | 1500 | 3000 | 3000 | 3000 |
| 笔记本电脑 | 1500 | 3000 | 4000 | 5500 |
这里最大价值也就是5500啦
代码:
#include<iostream>
using namespace std;
#define N 1001 //根据题目确认范围
int v[N],w[N];//v是物品体积,w是物品价值
int dp[N][N];//这就代表咱们之前画的小格子啦,在堆上默认所有值为0。dp[i][j] , i = 0也就是什么东西也不放,默认这一行都是等于0的呢。
int m,n;//n代表物品数量,m代表背包容积。
int main(){
cin>>n>>m;
for(int i = 1;i <= n ; i++) cin>>v[i]>>w[i];
for(int i = 1;i <= n;i++)
for(int j = 0;j <= m;j++){
dp[i][j] = dp[i-1][j]; //不选第i种物品的情况下的结果
if(j >= v[i]) dp[i][j] = max(dp[i][j],dp[i-1][j-v[i]]+w[i]);//选第i种物品的情况下的结果dp[i-1][j-v[i]]+w[i],与不选的情况做个比较
}
cout<<dp[n][m];
}
解析2-滚动数组优化
这是一个优化啦,因为我们可以看到,第i行需要进行操作的时候,只需要看第i-1行的数据就好啦,那我们可不可以只用一个一维数组来进行dp呢? 比如:
| 1磅 | 2磅 | 3磅 | 4磅 | |
|---|---|---|---|---|
| 手机 | 1500 | 1500 | 1500 | 1500 |
| 吉他 | ||||
| 笔记本电脑 |
此时数组为[1500,1500,1500,1500] 那么吉他行,先看4磅的时候放不放吉他,4磅的时候,看手机4磅的数与手机(4磅-1磅)3磅然后加上吉他的价值的内容,这样只需要更新吉他第四个数。然后看吉他3磅,这时候根本不用管手机4磅。所以理论上说,完全可以用一个数组来完成这个问题。
#include<iostream>
using namespace std;
#define N 1001 //根据题目确认范围
int v[N],w[N];//v是物品体积,w是物品价值
int dp[N];//这就代表咱们之前画的小格子啦,在堆上默认所有值为0。dp[i][j] , i = 0也就是什么东西也不放,默认这一行都是等于0的呢。
int m,n;//n代表物品数量,m代表背包容积。
int main(){
cin>>n>>m;
for(int i = 1;i <= n ; i++) cin>>v[i]>>w[i];
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]);
}
cout<<dp[m];
return 0;
}
在输入输出上下手,继续优化算法
#include<iostream>
using namespace std;
#define N 1001 //根据题目确认范围
int v,w;//v是物品体积,w是物品价值
int dp[N];//这就代表咱们之前画的小格子啦,在堆上默认所有值为0。dp[i][j] , i = 0也就是什么东西也不放,默认这一行都是等于0的呢。
int m,n;//n代表物品数量,m代表背包容积。
int main(){
cin>>n>>m;
for(int i = 1;i <= n;i++){
cin>>v>>w;
for(int j = m;j>=v;j--){
dp[j] = max(dp[j],dp[j-v] + w);
}
}
cout<<dp[m];
return 0;
}