OJ: T1268
有 N种物品和一个容量是 V 的背包,每种物品都有无限件可用。第 i 种物品的体积是 v[i],价值是 w[i],求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
测试数据:
输入:
4 6
1 2
2 4
3 8
4 10
输出:16
代码实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | /**************************************************************** * 代码作者: Alex Li * 创建时间: 2023-06-20 20:54:39 * 最后修改: 2025-01-31 23:33:19 * 文件描述: 这是一个完全背包问题的动态规划解法 O(N × m^2) ****************************************************************/ #include <iostream> using namespace std; const int MAXN=1000; const int MAXV=100; int dp[MAXN][MAXV]; int value[MAXV]; int weight[MAXN]; int main(){ int n,m,ik[MAXN][MAXV]; cin>>n>>m; for(int i=1;i<=n;i++)cin>>weight[i]>>value[i]; for(int i=1;i<=n;i++){ for (int j = 0; j <=m; j++){ //k是指i商品被用的件数,当k为0时,也就代表不选i商品,相当于初始化dp[i][j] for (int k = 0; k*weight[i]<=j; k++){ dp[i][j]=max(dp[i][j],dp[i-1][j-k*weight[i]]+k*value[i]); } } } cout<<dp[n][m]; return 0; } |
优化为二维DP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /**************************************************************** * 代码作者: Alex Li * 创建时间: 2025-01-31 22:23:43 * 最后修改: 2025-01-31 23:31:32 * 文件描述: 这是一个完全背包问题的动态规划解法 O(N × V) ****************************************************************/ #include <bits/stdc++.h> using namespace std; int complete_knapsack(int N, int V, vector<int> & w, vector<int> &v){ // dp[i][j] 表示前i个物品(物品1~i)、容量为j时的最大价值 vector<vector<int> > dp(N+1,vector<int>(V+1,0)); for(int i=1;i<=N;i++){ for(int j=0;j<=V;j++){ if(j<w[i]){dp[i][j]=dp[i-1][j]; }else{ // dp[i][j - w[i]] 表示在容量 j - w[i] 时已可能放入过当前物品,因此允许重复选取。 dp[i][j]=max(dp[i-1][j],dp[i][j-w[i]]+v[i]); } } } return dp[N][V]; } int main(){ int N=3; int V=5; // w和v数组改为1-based(索引0未使用) vector<int> w(N + 1, 0); // w[1]=2, w[2]=3, w[3]=4 vector<int> v(N + 1, 0); // v[1]=3, v[2]=4, v[3]=5 w[1] = 2; v[1] = 3; w[2] = 3; v[2] = 4; w[3] = 4; v[3] = 5; int result=complete_knapsack(N,V,w,v); cout<<"Maximum value:"<<result<<endl; } |
动态方程:dp[i][j] = max(dp[i-1][j], dp[i][j – weight[i]] + value[i])
dp[i][j] = max(
dp[i-1][j], // 不选物品i
dp[i][j - w[i]] + v[i] // 选至少一个物品i(依赖当前层已更新的状态)
)
j >= w[i]
时:
dp[i][j - w[i]]
表示在容量 j - w[i]
下,允许已经选取过物品i后的最优解。dp[i][j - w[i]] + v[i] > dp[i-1][j]
时,说明在当前容量 j
下,至少选取一个物品i比完全不选更优。j
保证了在计算 dp[i][j]
时,dp[i][j - w[i]]
可能已经包含了当前物品i的多次选取结果。例如:
dp[i][4]
时,dp[i][2]
可能已经通过 dp[i][0] + v[i] = 3
更新过。j
,且状态转移使用 dp[i-1][j - w[i]]
,确保每个物品只能被选一次。完全背包的正序设计打破了这一限制。也可以优化为一维动态数组:
for (int i = 1; i <= n; i++) { for (int j = weight[i]; j <= m; j++) { // 正序遍历 dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); } }