1.问题描述
2. 二维数组解法
2.1 思路分析
首先,分析题目,准备所需数据:
#define MAX0 101
int n,w; //n表示物品重量,w表示背包容量
int weght[MAX0]={0}; //物品重量
int value[MAX0]={0}; //物品价值
int dp[MAX0][MAX0]={0};
然后,确定选用的子问题,明确dp[][]数组的含义
本题目中,采用二维dp[][]数组,dp[i][j]的含义为 从前 i 件物品中任意取,放入容量为 j 的 背包中时,最大价值总和。
2.2 确定边界,初始化dp数组
从dp数组的定义出发,若 i 为0,则表示从前0件物品中任意选择放入背包,就是说不放入物品,所以总价值为0,dp[0][j]=0; 若 j 为0,则表示背包容量为0,无法放入物品,所以总价值也为0,dp[i][0]=0。即二维数组dp[][]的第一行和第一列都为0。
2.3 确定状态转移方程
(状态转移方程其实也可以理解为就是数学数列中的递推方程)
对于第 i 件物品,总有下图处理:
所以状态转移方程为:
dp[i][j]=dp[i-1][j];
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weght[i]]+value[i]);
2.4源码
#include<iostream>
#include<algorithm>
using namespace std;
#define MAX0 101
int n,w;
int weght[MAX0]={0},value[MAX0]={0},dp[MAX0][MAX0]={0};
int main()
{
//确定边界,初始化数组。
cin>>w>>n;
for(int i=1;i<=n;i++)
{
cin>>weght[i]>>value[i];
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=w;j++)
{
if(weght[i]>j) //判断背包容量是否足够放入 第i件物品。
{
dp[i][j]=dp[i-1][j];
}
else
{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weght[i]]+value[i]); //选择是否将第i件物品放入背包。
}
}
}
cout<<dp[n][w];
return 0;
}
3.一维数组解法
3.1思路分析
此方法也叫滚动数组法,DP的状态方程通常是二维和二维以上的,会占用很多空间。使用滚动数组可以大大减少占用空间。
从二维的状态转移方程 可以看出,dp[i][]只与dp[i-1][ ]有关,与前面的dp[i-2][ ]和dp[i-3][ ]都没有关系。由此看来前面的那些用过的数组就变得有些多余了,所以就可以重新使用这些空间,使用新的一组数据去覆盖上一组数据,让数组进行自我滚动。这样,使用一个一维dp[ ]数组就可以了。
数据准备只需更改dp数组即可。
#define MAX0 101
int n,w; //n表示物品重量,w表示背包容量
int weght[MAX0]={0}; //物品重量
int value[MAX0]={0}; //物品价值
int dp[MAX0]={0};
3.2 确定边界,初始化dp数组
当 j 为0时,背包容量为0,无法放入物品。dp[0]=0 。
3.3 确定状态转移方程
与二维相比,只是将数组参数 i 去掉。
对于第 i 件物品,依然总有下图处理:
3.4 更改遍历顺序
改使用滚动数组后,遍历顺序要发生相应改变。
外层循环,即对物品的循环顺序无需改变。
内层循环需要从后向前遍历。
图中用箭头标注了部分dp[][]数组的值是由上一层的哪个数据推出的。分析可知,总是要由上面一行的左边数据来推出下面一行右边的数据。
当改为一维数组时,下面一行的数据会将上面一行中对应位置的数据覆盖。
如果从前向后遍历,下面一行的数据就会将上面一行的数据覆盖,就会造成下面一行的一些数据计算时使用的不是上面一行的数据 。就可能导致出现错误。
而从后向前遍历则不会出现这种情况。下面一行右边的数据总是要由上面一行的左边数据来推出,当下面一行右边的推出之后,上面一行右边的数据就没用了,即使被覆盖也不会有影响。
3.5源码
#include<iostream>
#include<algorithm>
using namespace std;
#define MAX0 101
int n,w;
int weght[MAX0]={0},value[MAX0]={0},dp[MAX0]={0};
int main()
{
//确定边界,初始化数组。
cin>>w>>n;
for(int i=1;i<=n;i++)
{
cin>>weght[i]>>value[i];
}
for(int i=1;i<=n;i++)
{
for(int j=w;j>=1;j--)
{
if(weght[i]>j) //判断背包容量是否足够放入 第i件物品。
{
dp[j]=dp[j];
}
else
{
dp[j]=max(dp[j],dp[j-weght[i]]+value[i]); //选择是否将第i件物品放入背包。
}
}
}
cout<<dp[w];
return 0;
}
3.6一点小改动
可以将第二层循环的结束条件改为 j>=weght[i] ,这样循环体内就不需要判断物品 i 的重量是否小于当前背包的容量了。
#include<iostream>
#include<algorithm>
using namespace std;
#define MAX0 101
int n,w;
int weght[MAX0]={0},value[MAX0]={0},dp[MAX0]={0};
int main()
{
cin>>w>>n;
for(int i=1;i<=n;i++)
{
cin>>weght[i]>>value[i];
}
for(int i=1;i<=n;i++)
{
for(int j=w;j>=weght[i];j--)
{
dp[j]=max(dp[j],dp[j-weght[i]]+value[i]); //选择是否将第i件物品放入背包。
}
}
cout<<dp[w];
return 0;
}
如有不足,欢迎指正!