多重背包问题+题目

142 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第27天,点击查看活动详情

特点

  1. 有N个物品,容量是V的背包,每个物品有两个属性,体积Vi,价值Wi
  2. 每个物品个数限制不一样,每个物品最多有Si个
  3. 求:在背包能装的下的情况下的最大价值是多少

会讲一个朴素版跟优化版

题目

www.acwing.com/problem/con… image.png

分析

image.png image.png 状态计算:

  1. 第i个物品的选法,0个,1个,2个...但是个数不能超过s[i](有个数限制)

代码

朴素版

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 110;

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

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

题目

www.acwing.com/problem/con… image.png

优化做法:二进制优化方式

  1. 每种物品最多有Si个
  2. 将他们进行分组,拆分成logSi个组
    1. 分组的原理:
      1. 分组,每组分别只有这么多个物品:1, 2, 4, 8,...,2^k,C(常数),每组里面只能选一个物品
        1. C < 2^(k + 1)
        2. 1 + 2 + ……+2^k <= S
        3. 如果1 + 2 + …… + 2^k 之和比S小的话,要补一个C,使得1 + 2 + …… + 2^k + C= S
      2. 范围的扩展:0~1, 1
  3. 然后将拆分出来的组转化成01背包问题
  4. 时间复杂度:
    1. 没优化之前:N*V*S
    2. 优化之后:N*V*logS

代码

#include <iostream>
#include <algorithm>

using namespace std;

// N:物品个数1000 * log2000
const int N = 25000, M = 2010;

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

int main()
{
    cin >> n >> m;
    
    // 进行分组
    int cnt = 0; // 用一个变量来存储我们所有新的物品(分组后组内的物品)
    for (int i = 1; i <= n; i++)
    {
        int a, b, s; // 读入当前物品的体积,价值和个数
        cin >> a >> b >> s;
        int k = 1; // 从1开始分
        while (k <= s)
        {
            cnt ++; // 先将当前的物品编号++
            v[cnt] = a * k; // 分组后的物品的体积
            w[cnt] = b * k; // 分组后物品的价值
            s -= k;
            k *= 2;
             
        }
        if (s > 0) // 说明我们还要补上C
        {
            cnt ++;
            v[cnt] = a * s;
            w[cnt] = b * s;
            
        }
    }
    
    n = cnt;
    
    // 做一次01背包问题就行了
    for (int i = 1; i <= n; i++)
        for (int j = m; j >= v[i]; j--)
            f[j] = max(f[j], f[j - v[i]] + w[i]);
    
    cout << f[m] << endl;
}