题目描述:
- 有n个物品,每个物品有两个属性:一个是体积
wi,一个是价值
v1,可以表示为:
{(w1,v1),(w1,v1),…,(w1,vn)}。同时我们还有一背包,背包的容量用W表示。现在我们将物品放入背包,放入的物品体积的总和不得超过背包的体积。问这个背包能装下的最大价值。
限制条件:
-
1≤n≤100
-
1≤W≤109
-
1≤wi≤107
-
1≤vi≤100
解析:
一般的01背包:
- 先前写过一篇一般的01背包问题 的博客链接如下:https://blog.csdn.net/ACM_hades/article/details/89034229
- 题目描述是一样的,只是修改了限制条件,这篇博客考虑的是一个超大的背包。如果使用上一篇的动态规划,其实时间复杂度为
O(nW)=1011,这个时间复杂度就很高了。主要问题出在背包容量
W太大。
- 上一篇博客的
dp[i][j]:表示将第
i到第
n个物品装入容量为
j的背包中能得到的最大价值。也就是我们限制背包容量求最大价值,同时动态规划的时间复杂度也
i和
j的含义而确定。
- 其实我们发现:
dp[i][j]的含义主要由三部分确定:
-
i的含义
-
j的含义,
-
dp数组中存的值的含义。
- 并且
i,
j的取值范围决定了算法的时间复杂度。
- 例如在上一篇博客中:
i=物体索引(0-n),
j=背包容量(0-W),
dp=最大价值
- 为了降低时间复杂度我们必须要改变dp数组含义为。由于时间复杂度主要由
i和
j的含义决定,所以我们必须将
i和
j的含义于取值返回比较小的
n和
v数组 联系起来,将较大的
W与
dp数组中存的值联系起来。这样我们得到这样的
dp数组:
dp[i][j]=从前
i个物品中选择出价值为
j的物品的最小体积(若前
i个物品堆不出价值
j,则最小体积为
inf)
全新的动态规划:
- dp数组含义:
dp[i][j]=从前
i个物品中选择出价值为
j的物品的最小体积(若前
i个物品堆不出价值
j,则最小体积为
inf)
- 初始条件:
dp[0][0]=0;dp[0][1−∑i=1nvi]=inf
- 递推公式:
dp[i][j]=min{dp[i−1][j],dp[i−1][j−v[i]]+w[i]}
- 递推方向:从上到下,即只要知道第
i行就能求出第
i+1行。
- 结果:为
dp中第
n行中小于等于
W的元素的最大列号,即
max{j∣dp[n][j]≤W}
代码:
#include <iostream>
#include <string.h>
using namespace std;
#define Max_n 105
#define Max_v 105
#define Inf 1000000005
int n,W;
int w[Max_n],v[Max_n];
int dp[Max_n][Max_n*Max_v+1];
void Dynamic_degradation(){
fill(dp[0],dp[0]+Max_n*Max_v,Inf);
dp[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=Max_n*Max_v;j++){
dp[i][j]=dp[i-1][j];
if(j>=w[i])
dp[i][j]=min(dp[i][j],dp[i-1][j-v[i]]+w[i]);
}
}
int result=0;
for(int i=0;i<=Max_n*Max_v&dp[n][i]<=W;i++)
if(i>result)
result=i;
cout<<result<<endl;
}