完全背包问题,在已知物品个数( i )和最大容量( j )后,从i个物品中选取最大价值总和。(每个物品可以选取无数次)
与01背包问题所不同的就在这个每个物品可以选取多次上了,原有的状态转移方程加上选取当前物品的个数变量( knd ),即在满足k[ind]<=jnd时,状态转移方程为dp[ind][jnd]=Max(dp[ind-1][jnd-knd*k]+knd*value,dp[ind-1][jnd]),0<=knd*k<=jnd。与01背包问题相同,k[ind]>ind时,dp[ind][jnd]=dp[ind-1][jnd]。
稍稍改动01背包基本法即可。
#include<stdio.h>
#define Max(a,b) (a>b?a:b)
int dp[1005][10005];
int k[1005], value[1005];
int main(int argc, char* argv[])
{
int i, j, s;
scanf("%d %d", &i, &j);
for (int ind = 1; ind <= i; ++ind)
scanf("%d %d", &k[ind], &value[ind]);
for (int ind = 1; ind <= i; ++ind)
{
for (int jnd = 1; jnd <= j; ++jnd)
{
if (jnd - k[ind] < 0)
dp[ind][jnd] = dp[ind - 1][jnd];
else
{
s = dp[ind - 1][jnd];
for (int knd = 1; knd*k[ind] <= jnd; knd++)
s = Max(s, dp[ind - 1][jnd - knd * k[ind]] + knd * value[ind]);
dp[ind][jnd] = s;
}
}
}
printf("%d\n", dp[i][j]);
return 0;
}
在递增knd求取最大值的时候,必须要另建一个变量,否则可能出现都比dp[ind-1][jnd]大,导致dp[ind][jnd]最终值是knd最大时的价值而非价值最大的情况!
滚动数组法改起来就更简单了,01背包问题时必须从后往前滚动,从而保证每次都是历史数据。完全背包问题只需反过来,从前往后滚动即可,这样每次滚动用到的dp[ind-k]就都是最新的数据了,也就是从可以取当前物品更新最大值的最低容量开始更新最大值,虽然每次只能在原有基础上加上一个当前物品,但是小容量的背包已经根据当前物品更新过了最大值,所以较大容量的背包如果满足更新最大值条件,也就是对应的容量加上了最合适数目的当前物品。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Max(a,b) (a>b?a:b)
int dp[10005];
int main(int argc, char* argv[])
{
int i, j;
scanf("%d %d", &i, &j);
while (i--)
{
int k, value;
scanf("%d %d", &k, &value);
for (int ind = k; ind <= j; ind++)
dp[ind] = Max(dp[ind - k] + value, dp[ind]);
}
printf("%d\n", dp[j]);
return 0;
}
滚动数组法比基本法写起来省力,复杂度也低,是基础背包问题首选方法。