完全背包问题
1.1 题目描述
- 有N中物品和一个容量为V的背包。每种物品都有无限件。第i种物品体积为w[i],价值是v[i]。要求放入背包中的物品价值总和是最大的。
1.2 基本思路
- 这个问题和上篇讲过的01背包问题有点像,不同的是每件物品都有无限件。按照01背包的思路,每件物品就不再是取或者不取的状态,而是可以取多件。背包不是无限大,第i物品最多只能取V/w[i]件。用dp[i][j]来表示前面i件物品放入一个容量为j的背包中价值总和最大是多少。可以写出一个状态转移方程
d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j − k ∗ w [ i ] ] + k ∗ v [ i ] ∣ 0 = < k ∗ w [ i ] < = V } dp[i][j] = max\{dp[i-1][j-k*w[i]] +k*v[i] |0=<k*w[i]<=V\} dp[i][j]=max{dp[i−1][j−k∗w[i]]+k∗v[i]∣0=<k∗w[i]<=V}
代码
#include<stdio.h>
#include<string.h>
int max(int a, int b)
{
return a > b ? a : b;
}
int dp[1005][1005];
int main()
{
int N, V;
int v[1005];
int w[1005];
memset(dp, 0, sizeof(dp));
scanf("%d %d\n", &N, &V);
for(int i = 1; i <= N; i++)
scanf("%d %d", &w[i], &v[i]);
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= V; j++)
{
for(int k = 0; k <= j/w[i]; k++)
{
if(j < w[i]) dp[i][j] = dp[i-1][j];
else
dp[i][j] = max(dp[i][j], dp[i-1][j - k * w[i]] + k * v[i]);
}
}
}
printf("%d\n", dp[N][V]);
return 0;
}
1.3 时间复杂度优化
-
上述方法的时间复杂度为O(VNΣ W/v[i]),是比较大的。将上述状态转移方程进行优化
d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j − k ∗ w [ i ] ] + k ∗ v [ i ] ∣ 0 < = k } d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j ] , m a x { d p [ i − 1 ] [ j − k ∗ w [ i ] ] + k ∗ v [ i ] ∣ 1 < = k } } d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j ] , m a x { d p [ i − 1 ] [ j − w [ i ] − k ∗ w [ i ] ] + k ∗ v [ i ] + v [ i ] ∣ 0 < = k } } d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j ] , m a x { d p [ i − 1 ] [ j − w [ i ] − k ∗ w [ i ] ] + k ∗ v [ i ] ∣ 0 < = k } } + v [ i ] d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j ] , d p [ i ] [ j − w [ i ] ] + v [ i ] } dp[i][j] = max\{dp[i-1][j-k*w[i]]+k*v[i]|0<=k\}\\ dp[i][j] = max\{dp[i-1][j], max\{dp[i-1][j-k*w[i]] + k * v[i] | 1<=k\}\}\\ dp[i][j] = max\{dp[i-1][j], max\{dp[i-1][j-w[i]-k*w[i]] + k * v[i] + v[i] | 0<=k\}\}\\ dp[i][j] = max\{dp[i-1][j], max\{dp[i-1][j-w[i]-k*w[i]] + k * v[i]| 0<=k\}\}+v[i]\\ dp[i][j] = max\{dp[i-1][j], dp[i][j-w[i]]+v[i]\} dp[i][j]=max{dp[i−1][j−k∗w[i]]+k∗v[i]∣0<=k}dp[i][j]=max{dp[i−1][j],max{dp[i−1][j−k∗w[i]]+k∗v[i]∣1<=k}}dp[i][j]=max{dp[i−1][j],max{dp[i−1][j−w[i]−k∗w[i]]+k∗v[i]+v[i]∣0<=k}}dp[i][j]=max{dp[i−1][j],max{dp[i−1][j−w[i]−k∗w[i]]+k∗v[i]∣0<=k}}+v[i]dp[i][j]=max{dp[i−1][j],dp[i][j−w[i]]+v[i]}
这样时间复杂度就从三次降到了两次
代码
#include<stdio.h>
#include<string.h>
int max(int a, int b)
{
return a > b ? a : b;
}
int dp[1005][1005];
int main()
{
int N, V;
int v[1005];
int w[1005];
memset(dp, 0, sizeof(dp));
scanf("%d %d\n", &N, &V);
for(int i = 1; i <= N; i++)
scanf("%d %d", &w[i], &v[i]);
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= V; j++)
{
if(j < w[i]) dp[i][j] = dp[i-1][j];
else dp[i][j] = max(dp[i-1][j], dp[i][j-w[i]] + v[i]);
}
}
printf("%d\n", dp[N][V]);
return 0;
}
1.4 空间复杂度优化
dp[i][j]只依赖于dp[i-1][j]和dp[i][j-w[i]],可以用滚动数组优化空间
代码
#include<stdio.h>
#include<string.h>
int max(int a, int b)
{
return a > b ? a : b;
}
int dp[2][1005];
int main()
{
int N, V;
int v[1005];
int w[1005];
memset(dp, 0, sizeof(dp));
scanf("%d %d\n", &N, &V);
for(int i = 1; i <= N; i++)
scanf("%d %d", &w[i], &v[i]);
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= V; j++)
{
if(j < w[i]) dp[i%2][j] = dp[(i-1)%2][j];
else dp[i%2][j] = max(dp[(i-1)%2][j], dp[i%2][j-w[i]] + v[i]);
}
}
printf("%d\n", dp[N%2][V]);
return 0;
}
1.5 小结
做动态规划的题目,推出状态转移方程以及其中的意思是非常重要的