背包问题——完全背包(自用)

文章讲述了如何使用动态规划解决完全背包问题,通过状态转移将0/1背包中的k循环简化,最后利用滚动数组进一步优化算法,降低时间复杂度。
摘要由CSDN通过智能技术生成

题目

N N N种物品和一个容量是 V V V的背包,每种物品都有无限件可用。
i i i 种物品的体积是 v i v_i vi,价值是 w i w_i wi
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。

输入格式
第一行两个整数, N N N V V V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N N N 行,每行两个整数 v i v_i vi, w i w_i wi,用空格隔开,分别表示第 i i i 种物品的体积和价值。
输出格式
输出一个整数,表示最大价值。

分析

状态表示

用一个数组f[i][j]来表示只看前i个物品,在总体积是j的情况下,总价值最大是多少。
那么,题目要求在背包容量为V的条件下,从N件物品中选取物品使得的总价值最大,状态表示应为f[N][V].

状态计算

在进行状态计算之前,先得对状态做一个划分。对于01背包,f[i][j]的状态只有两种:

不选第i个物品选第i个物品
f[i-1][j]f[i-1][j-v[i]] + w[i]

取两种状态的最大值
对于完全背包,物品可以取无限个,不过k最多到k*w=v时为止,因为不能超背包容量
那么状态可以划分为:

不选第i个物品(选0个)选1个第i个物品选2个第i个物品选k个第i个物品
f[i-1][j]f[i-1][j-v[i]] + w[i]f[i-1][j-2*v[i]] + 2*w[i]f[i-1][j-k*v[i]] + k*w[i]

找到这些状态的最大值即可。
因此,我们只需要在0-1背包的基础上,多循环一次k

#include<iostream>
using namespace std;

const int N = 1010;

int n, m;
int f[N][N], v[N], w[N];

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i ++ )
        cin >> v[i] >> w[i];
    for(int i = 1; i <= n; i ++ )
        for(int j = 0; j <= m; j ++ )
            for(int k = 0; k * v[i] <= j; k ++ )
                f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
    cout << f[n][m] << endl;
}

优化

上面的算法时间复杂度过高。
回顾下状态切分 对于f[i][j]来说

不选第i个物品选1个第i个物品选2个第i个物品选3个第i个物品
f[i-1][j]f[i-1][j-v[i]] + w[i]f[i-1][j-2*v[i]] + 2*w[i]f[i-1][j-3*v[i]] + 3*w[i]

而巧就巧在f[i][j-v[i]]的状态划分为

不选第i个物品选1个第i个物品选2个第i个物品
f[i-1][j-v[i]]f[i-1][j-2*v[i]] + w[i]f[i-1][j-3*v[i]] + 2*w[i]

我们发现,f[i][j]中选择1–k个第i个物品和f[i][j-v[i]]中选择k–1个第i个物品正好是对应的(只相差w[i])
因此,对于f[i][j],选择k个第i个物品的最大值为f[i][j-v[i]] + w[i],我们不再需要循环k个物品了,状态划分简化为:

不选第i个物品选择k个第i个物品
f[i-1][j]f[i][j-v[i]] + w[i]

取二者的最大值即可。
代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1002;
int n, m;
int f[N][N];
int v[N], w[N];

int main(){
    cin >> n >> m;
    for (int i = 1; i <= n; i++) 
        scanf("%d%d", &v[i], &w[i]);
    
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            f[i][j] = f[i-1][j];
            if (j >= v[i]) {
                f[i][j] = max(f[i][j], f[i][j-v[i]] + w[i]);
            }
        }
    }
    
    cout << f[n][m] << endl;
    
    return 0;
}

可以用滚动数组,再次优化。

  • 23
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值