关于背包问题一维解法,网上一堆各种讲解资料,笔者在参考了很多资料后,总结出了一份较简单直观便于理解的一维解法的讲解。
老规矩,我们还是从题切入,先看题目要求:
本文只讲解背包问题的一维解法,关于背包问题动态规划的二维解法,详见上一篇文章:最简单直观详细的理解 背包问题 的一维解法 动态规划_一维背包问题_Bule Guy的博客-CSDN博客
首先,对于背包问题的二维解法,对于f[i][j],我们可以得到一个二维表格
然后根据二维公式f[i][j]=max{ f[i-1][j], f[i-1][j-v[i]]+w[i] },我们可以填满该二维表格。
该办法的缺点就是,按照题意我们只需要装满包的容量那个结果,即f[N][V],但是我们观察二维表格数据可以得出一个非常直观的规律,我们填二维表格的下一行的数据,是需要二维表格上一行数据才能计算出来的。
所以我们需要用二维数组将前面所有数据计算出来f[N][V]。
但是因为我们只需要前一行的数据来计算下一行的数据,那么为了节省空间,我们其实可以只保存当前行和上一行两行数据即可,之前的数据我们用完后直接覆盖掉不就可以了吗?
所以我们可以直接用一维数组进行优化
我们定义一维数组 f[j]:N件物品,背包容量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] } 可以参考对比下有什么异同)
另外,还有一点和二维数组不一样的是:一维要采取逆序的方式来循环,为什么呢,
我们举例说明:
假如枚举到:i = 3, j = 8, v[3] = 5, w[3] = 1
二维:dp[3][8] = max(dp[2][8], dp[2][3] + w[3]) 此时的dp[2][8]和dp[2][3]都是上一轮的状态值
一维:dp[8] = max(dp[8], dp[3] + w[3]) 我们要保证dp[8]和dp[3]都是上一轮的状态值
按照逆序的顺序,一维dp数组的更新顺序为:dp[8], dp[7], dp[6], ... , dp[3]
也就是说,在本轮更新的值,不会影响本轮中其他未更新的值!较小的index对应的状态是上一轮的状态值!
如果按照顺序进行更新,dp[3] = max(dp[3], dp[0] + w[0]),对dp[3]的状态进行了更新,那么在更新dp[8]时,用到的dp[3]
就不是上一轮的状态了,不满足动态规划的要求。
所以代码为:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1010;
int n,m;//n个物品,背包容量为m
int f[N];
int v[N],w[N];//物体的体积和价值
int main(){
cin>>n>>m;
//输入物体体积和价值
for(int i=1;i<=n;i++)//数列下标从1开始,而不是0
{
cin>>v[i]>>w[i];
}
for(int i = 1; i <= n; i++)
for(int j = m; j >= 0; j--) //注意这里是逆序
{
if(j < v[i])
// f[i][j] = f[i - 1][j]; 二维时的做法
f[j] = f[j]; // 一维优化后,该行自动成立,可省略。
else
// f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]); 二维时的做法
f[j] = max(f[j], f[j - v[i]] + w[i]); // 一维优化后
}
//输出最终结果
cout<<f[m]<<endl;
return 0;
}