01背包及滚动数组优化(一维)

01背包问题

  • 有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
  • 第 i 件物品的体积是 vi,价值是 wi。
  • 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。
输入格式
  • 第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
  • 接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式 输出一个整数,表示最大价值。
数据范围 0 0 输入样例 4 5
1 2
2 4
3 4
4 5
输出样例: 8
代码
其中,初始化的部分也可以不写,因为数组定义在main函数外面,自动初始化为0了
#include #define maxn 1010 #define max(a,b) a>b?a:b int v[maxn],w[maxn]; //v[i],w[i]分别表示第i件物品的体积和价值。 int dp[maxn][maxn]; //dp[i][j]表示前i件物品,体积刚好为j时最大价值。 int main() { int n,V; //n为物品数量,v为背包容积 scanf("%d%d",&n,&V); for(int i=1; i<=n; i++) { scanf("%d%d",&v[i],&w[i]); } for(int i=0; i<=n; i++) dp[i][0]=0; //边界条件 for(int i=0; i<=V; i++) dp[0][i]=0; //边界条件 for(int i=1; i<=n; i++)//i表示前i件 { for(int j=1; j<=V; j++) //j表示体积刚好为j { dp[i][j]=dp[i-1][j]; if(j>=v[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]); } } int sum=0; for(int j=1; j<=V; j++) { sum=max(dp[n][j],sum); } printf("%d\n",sum); return 0; }

对于01背包问题,需要dp[n][v]数组,即需要n*m大小的空间,如果遇到较大的数据就会被善良的出题人卡掉,那么如何进行优化呢?
#include #define maxn 1010 #define max(a,b) a>b?a:b int v[maxn],w[maxn]; //v[i],w[i]分别表示第i件物品的体积和价值。 int dp[maxn]; //dp[i][j]表示前i件物品,体积刚好为j时最大价值。 int main() { int n,V; //n为物品数量,v为背包容积 scanf("%d%d",&n,&V); for(int i=1; i<=n; i++) { scanf("%d%d",&v[i],&w[i]); } for(int i=1; i<=n; i++)//i表示前i件 { for(int j=V; j>=v[i]; j--) //j表示体积刚好为j { dp[j]=max(dp[j],dp[j-v[i]]+w[i]); } } int sum=0; for(int j=1; j<=V; j++) { sum=max(dp[j],sum); } printf("%d\n",sum); return 0; }

是不是很神奇,只需要把第一维全部砍掉,再把j的范围改为从后往前,j–。是什么原理呢
原理(滚动数组)
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])
注意到这个二维数组的下一行的值,只用到了上一行的正上方和左边的值,因此可用滚动数组的思想,只要一行即可。
【01背包及滚动数组优化(一维)】既然是要用正上方和左边的值,那么从右边开始依次求值,并覆盖在正上方的位置即可
01背包及滚动数组优化(一维)
文章图片

01背包问题题目链接

    推荐阅读