背包问题核心
动态规划的一个特点就是当前解可以由上一个阶段的解推出
优化一般就是优化状态转移方程
01背包问题
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8
特点:每个物品仅能使用一次
重要变量&公式解释
f[i][j]:表示所有选法集合中,只从前i个物品中选,并且总体积≤≤j的选法的集合,它的值是这个集合中每一个选法的最大值.
状态转移方程
f[i][j] = max(f[i-1][j], f[i-1][j-v[i]]+w[i])
f[i-1][j]:不选第i个物品的集合中的最大值
f[i-1][j-v[i]]+w[i]:选第i个物品的集合,但是直接求不容易求所在集合的属性,这里迂回打击一下,先将第i个物品的体积减去,求 从前i-1个物品中选,并且总体积≤j-v[i]的选法的集合中选法的最大值然后再加上第i个物品的权重即可
问题
集合如何划分
一般原则:不重不漏,不重不一定都要满足(一般求个数时要满足)
如何将现有的集合划分为更小的子集,使得所有子集都可以计算出来.
作者:竹林正在青
链接:https://www.acwing.com/solution/content/4515/
//二维写法
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int f[N][N];
int v[N],w[N];
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=1;j<=m;j++){
if(j<v[i]){
f[i][j]=f[i-1][j];
}
else {
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
}
}
}
cout << f[n][m];
return 0;
}
今天终于把二维转一维理解了 赶紧记录下来
首先二维为什么能转一维呢?
因为每次只用到i-1的状态,也就是上一个状态,i-2及其之前的都用不到了,所以可以用一维来优化用的是滚动数组思想;
为什么二维转一维需要从大到小枚举呢?
首先,我们要明白 f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])
f[i][j]由这二个状态更新而来,也就是说用的是第i-1轮的状态,
如果我们从小到大枚举 f[j]=max(f[j],f[j-v[i]]+w[i])这里因为没有i,所以我们并不知道
更新f[j]的用的是第i-1轮的状态,还是第i轮的状态,
为什么会可能从第i轮的状态更新而来呢?
我们假设f[7]由f[4]更新而来,那么f[4]很可能已经在第i轮更新过一次了,也就是
f[i-1][4]已经被更新成f[i][4]了
而我们从大到小枚举就不会出现这种情况我们知道f[较大体积]由f[较小体积]更新而来,因为
从大到小枚举,更新f[较大体积]所需要的的f[较小体积]一定还没被更新过。。。。
以上
//一维写法,滚动数组思想,保留第二维,优化掉了第一维
/*
1. f[i] 仅用到了f[i-1]层,
2. j与j-v[i] 均小于j
3.若用到上一层的状态时,从大到小枚举, 反之从小到大
*/
#include<iostream>
using namespace std;
const int N=1010;
int n,m;
int f[N];
int v[N],w[N];
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--){
// 这里可以发现当j<v[i]时候,按正常来说要进行判断执行f[j]=f[j],可以看出这一步完全可以省略,所以事实上,在一维状态下,只有当j>=v[i]的时候,f[j]才会被更新
f[j]=max(f[j],f[j-v[i]]+w[i]); // 优化前为f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
}
}
cout <<f[m];
return 0;
}
01背包问题分析过程,依据题中给的样例分析
#include<bits/stdc++.h>
using