动态规划完全背包问题主要指的是:1.给定一个容量为max_weight的背包,每一种物品的质量为weight[i],价值为value[i],且每一种物品的个数有无限个,问:如何放置可以使得背包价值最大?2.可以转换为此类问题的题目。
一 典型问题
假设有一个质量为5的背包和3种物品,且每个物品的质量都不一样,这组物品的质量和价值为weights=[1,2,3],values=[2,4,6],且每种物品数量有无数个。问:如何放入物品可以使得背包中的总价值最大?
1.1 分析
此类问题与零一背包问题相比,主要区别在于遍历顺序上面:
零一背包在遍历背包容量时,是从前往后进行遍历的,这样遍历的原因是,如果从前往后遍历的话,由于递推公式是dp[i] = max(dp[i], dp[i - weight[j]] + value[j]),由于每种物品的个数是1个,然而当前dp数组的值也会由之前的dp数组dp[i - weight[j]]决定,因此这样子可能会导致同一种物品被重复放入两次或者以上。
然而对于完全背包,由于每种物品数量有无限多个,因此,一般是从前往后遍历背包容量。
1.2 代码
/*背包的容量为max_weight,物品质量为weights,物品价值为values*/
int DP_totalbag(int max_weight, vector<int> weights, vector<int> values)
{
vector<int> dp(max_weight + 1, 0);
for(int i = 0;i < weight.size();i++)//遍历物品
{
for(int j = weight[j];j <= max_weight;j++)//遍历背包
dp[j] = max(dp[j], dp[j - weight[i]] + values[i]);
}
return dp[max_weight];
}
二 组合问题与排列问题
由于对于完全背包问题,如果问题是背包最多能够装多少东西或者背包中价值最大为多少的问题,对于遍历顺序是先遍历背包再遍历物品还是先遍历物品再遍历背包是没有区别的。
不过,如果问题改为:1.将背包填满的物品组合数(排列顺序不一样也认为是一个组合);2.将背包填满的物品排列数 这两类问题的遍历顺序是由讲究的:
2.1 分析
对于排列问题,如果先遍历物品,再遍历背包,假设物品质量分别为weights = [1,5],则最终遍历的结果只有[1, 5]这一种排列情况,不会出现[5, 1]这种排列情况,所以,这种遍历顺序是用来计算组合数的,对于排列数的遍历顺序应该是先遍历背包再遍历物品。
2.2 代码
对于组合数问题,代码如下所示:
/*背包的容量为max_weight,物品质量为weights*/
int DP_totalbag(int max_weight, vector<int> weights)
{
vector<int> dp(max_weight + 1, 0);
dp[0] = 1;
for(int i = 0;i < weights.size();i++)//遍历物品
{
for(int j = weights[i];j <= max_weight;j++)//遍历背包
dp[j] += dp[j - weights[i]];
}
return dp[max_weight];
}
对于排列数问题,代码如下所示:
/*背包的容量为max_weight,物品质量为weights*/
int DP_totalbag(int max_weight, vector<int> weights)
{
vector<int> dp(max_weight + 1, 0);
dp[0] = 1;
for(int i = 0;i <= max_weight;i++)
{
for(int j = 0;j <= weights.size();j++)
if(i - weights[j] >= 0) dp[i] += dp[i - weights[j]];
}
return dp[max_weight];
}