多重背包问题描述:介于01背包和完全背包问题之间,每种物品的最大选取数目都是已知的。
对于一定数量( i )的物品有一个容量为( j )的背包,每个物品都有自己的容量( k )、价值(value)和数目( cnt )。在保证物品容量之和不大于背包容量的前提下,如何选取物品得到最大价值?
状态转移方程可以稍微修改完全背包问题的得到,dp[ind][jnd]=Max(dp[ind-1][jnd-knd*k]+knd*value,dp[ind-1][jnd]),原方程不变,需要改的是knd的范围,完全背包每个物品的个数是无限个,knd仅需要满足knd*k<=jnd。现在每个物品有了自己的最大数目( cnt ),也就是说还需要满足knd<=cnt。
经过上面的推论,在 jnd<k 时,dp[ind][jnd]=dp[ind-1][jnd] ;jnd>=k时,dp[ind][jnd]=Max(dp[ind-1][jnd-knd*k]+knd*value,dp[ind-1][jnd]) | knd<=cnt | knd*k<=jnd 。
#include<stdio.h> #define Max(a,b) (a>b?a:b) int dp[1005][10005]; int k[1005], value[1005], cnt[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 %d", &k[ind], &value[ind], &cnt[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 <= cnt[ind]; 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; }
不经优化的状态转移方程直接求解时间复杂度太高,大多需求都不能满足。考虑能不能通过什么方法
#include<stdio.h> #include<stdlib.h> #include<string.h> #define Max(a,b) (a>b?a:b) #define ArrayMax 10005 int dp[ArrayMax]; int main(int argc, char* argv[]) { int i, j; scanf("%d %d", &i, &j); while (i--) { int k, value, cnt; int now = 1; scanf("%d %d %d", &k, &value, &cnt); if (cnt*k > j) { for (int ind = k; ind <= j; ind++) dp[ind] = Max(dp[ind - k] + value, dp[ind]); } else { while (1) { if (cnt > now) { cnt -= now; for (int ind = j; ind >= k * now; ind--) dp[ind] = Max(dp[ind - k * now] + now * value, dp[ind]); now *= 2; } else { now = cnt; for (int ind = j; ind >= now * k; ind--) dp[ind] = Max(dp[ind - now * k] + now * value, dp[ind]); break; } } } } printf("%d\n", dp[j]); return 0; }(草稿)