多重背包问题(12-12)

112 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 12 天,点击查看活动详情

今天学习了简单和中等的多重背包模型。

多重背包 I

题目描述

有 NN 种物品和一个容量是 VV 的背包。

第 ii 种物品最多有 sis_i 件,每件体积是 viv_i,价值是 wiw_i

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数,N,VN,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 NN 行,每行三个整数 Vi,wi,siV_i,w_i,s_i,用空格隔开,分别表示第 ii 种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V1000<N,V≤100

0<vi,wi,si1000<v_i,w_i,s_i≤100

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

题目分析

由于多重背包问题对每件物品有数量的限制,所以我们可以将体积为 viv_i 的物品具有 sis_i 件分开,即每个物品作为一个单独的个体,这样我们就可以转化为总个数为 s1+s2++sns_1+s_2+\dots+s_n01背包问题

Accept代码 O(nvs)

#include <iostream>

using namespace std;

const int N = 110;

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

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

多重背包问题 II

数据范围

0<N10000<N≤1000

0<V20000<V\le2000

0<vi,wi,si20000<v_i,w_i,s_i≤2000

题目分析

本题题干与上一题一致,唯一的区别为扩大了数据的范围,若仍使用上题解法则会超时。

这里采取一种二进制优化的方案。

在朴素做法中,我们对于数量为 ss 的物品采用从 00ss 依次枚举数量的方式。

在优化中,我们将总数为 ss 的物品以 1,2,42k1,s(2k1)1,2,4\dots2^{k-1},s-(2^k-1), 其中 2k1s<2k+112^k-1\le s < 2^{k+1}-1 件数重新划分,可以证明让新划分的物品重新组合可以还原为原物品范围件数。

题目复杂度由 O(nvs)O(nvs) 优化为 O(nvlogs)O(nvlogs)

Accept代码 O(nvlogs)

#include <bits/stdc++.h>

using namespace std;

const int N = 20010, M = 2010;
int v[N], w[N], f[M];
int cnt;

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < n; i ++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        int k = 1;
        while (c >= k)
        {
            v[cnt] = k * a;
            w[cnt] = k * b;
            c -= k;
            k *= 2;
            cnt ++;
        }
        if (c) v[cnt] = c * a, w[cnt ++] = c * b;
    }
    
    n = cnt;
    for (int i = 0; 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];
    return 0;
}