01背包问题

01背包问题是经典的动态规划问题,问题描述如下:


我们用f[i][j]表示将前i个物品放入容量为j的背包里所能获得的最大价值,则状态转移方程如下:


具体程序如下:

#include "stdafx.h"
#include<iostream>
#include<vector>
using namespace std;
int maxValue(vector<int>weight,vector<int>value,int bagCapacity)
{
	int m=weight.size();
	int n=bagCapacity;
	vector<vector<int>>dp(m+1,vector<int>(n+1,0));
	vector<int> rollDp(n+1,0);
	for(int i=0;i<m;++i)
	{
		for(int j=n-1;j>=0;--j)
		{
			if(weight[i]<=j+1)
			{
				dp[i+1][j+1]=max(dp[i][j+1],dp[i][j+1-weight[i]]+value[i]);  //当内循环从右到左时,可以用滚动数组来优化空间复杂度。
				rollDp[j+1]=max(rollDp[j+1],rollDp[j+1-weight[i]]+value[i]); //用于滚动数组降低空间复杂度
			}
			else
			{
				dp[i+1][j+1]=dp[i][j+1];
			}

		}
	}
	cout<<"滚动数组所得结果为: "<<rollDp[n]<<endl;  //滚动一维数组输出结果和二维数组一致
	return dp[m][n];
}
int main()
{ 
	int numCnt=0;   //物品数目
	int bagCapacity=0;  //背包容量
	while(cin>>numCnt>>bagCapacity)
	{
		vector<int>weight(numCnt,0);
		vector<int>value(numCnt,0);
		for(int i=0;i<numCnt;++i)
		{
			cin>>value[i];
		}
		for(int i=0;i<numCnt;++i)
		{
			cin>>weight[i];	
		}
		cout<<"最大价值为: "<<maxValue(weight,value,bagCapacity);
	}
}
程序运行结果如下:


上述输入第一行第一个数代表物品个数,第二个数代表背包最大容量;第二行每个数分别代表对应的物品的价值,第三行每个数分别代表对应物品的重量。

从程序中我们可以看到如果利用二维数组dp来编写程序,就是完全按照我们之前写的状态转移方程来写,这个很好理解,但是空间复杂度为O(N*W)。我们注意到状态转移方程中dp[i][j]只与dp[i-1][]有关(第二维空着不是笔误哦,我是想表达的是外层的第i个循环的值,只与外层的第i-1个循环的值决定,这样一来,内层循环不管顺序还是逆序都可以,自己思考一下为什么?),而当内层循环逆序的时候,我们通常可以用一维数组来代替二维数组,原因如下:这种情况下我们可以用dp[j]取代dp[i][j]是因为计算dp[i][j]前,我们已经计算了dp[i-1][j],所以如果我们之前是用的一维数组dp[j]的话,那计算dp[i][j]前一瞬间,dp[j]代表的是dp[i-1][j],dp[i][j]计算完后,dp[j]表示的就是dp[i][j],程序中我将这两种方法写到了一起,方便领悟其中的差别。现在我们思考一个问题,为什么用一维数组的时候,内层循环必须是逆序,不能顺序?原因是

dp[i+1][j+1]=max(dp[i][j+1],dp[i][j+1-weight[i]]+value[i]),注意到式中有dp[i][j+1-weight[i]],如果假设j+1-weight[i]=3,那当我们求dp[i+1][j+1],而j=6时,此时用二维数组肯定不会有问题,但是用一维数组且内循环顺序增长时,此时dp[j+1]即dp[7]可能与dp[3]有关,但此时的dp[3]已经是dp[i+1][3]了,而不再是dp[i][3],违背了我们的状态转移方程。




  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值