有关多重背包的二进制优化

算法相关的学习笔记与代码记录。

有关多重背包的二进制优化 的统一封面

原题:混合背包问题

在本题中 n ∈ [1, 1000], m ∈ [1, 2000], s ∈ [1, 2000]

若使用传统多重背包写法:
三重循环嵌套-时间复杂度为:
枚举n件物品 * 枚举背包体积v * 每件物品数s
时间复杂度O(nms)O(nms) \quad这显然是我们不可以接受的

但是每次第三层循环都要从1−>s1->s把物品所有可能数量枚举一遍
或许我们可以考虑将ss件物品等效,使得ss的数量减少,且原本可以由s件物品选出的ii仍可以被表示出
我们考虑将s打包为1,2,4,8...Ai{1,2,4,8...Ai} 使得
∑ai=s∑ai = s 则任意数 ∈[1,s]∈ [1, s] 均可由a中数字组合得出
于是将其打包成物品集合aa 跑一遍01背包即可

//算法提高课背包dp
//混合背包化作多重背包

//11/26 10/13 TLE
//11/26 10/13 WA : 二进制优化
//11/26 Ac : 个数开到nlogn 即 N = log1e3 * 1e3 ~= 1e4

#include<iostream>
#include<cstdio>

using namespace std;

const int N = 1e4 + 10;

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

int main(){
    freopen("bag.in", "r", stdin);
    freopen("bag.out", "w", stdout);
    
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++){
        int W, V, S;
        scanf("%d%d%d", &W, &V, &S);
        if(S == -1)S = 1;
        else if(S == 0)S = m / W;
        
        int k = 1;
        while(k <= S){
            w[++cnt] = k * W;
            v[cnt] = k * V;
            S -= k;
            k *= 2;
        }
        if(S){
            w[++cnt] = S * W;
            v[cnt] = S * V;
        }
    }
    
    n = cnt;
    
    for(int i = 1; i <= n; i ++){
        for(int j = m; j >= w[i]; j --){
            f[j] = max(f[j], f[j - w[i]] + v[i]); 
        }
    }
    
    printf("%d", f[m]);
    
    return 0;
}

互动

0 人点赞 · 0 条评论


© 2026 By Catkin

Powered by Astro