背包问题 多重背包二进制之我见。

大佬讲解
我再说一下我对二进制以下的理解。
Q:所谓将num拆分成k个二进制数,那么为什么这些二进制数可以表示1~num的任意一个数?
A:同拆分成num个01背包一样,它的原理是由num个1,每一个代表一个物体,用这些物体来表示num个物体的情况。那拆分成k个二进制数不也同样的意思这k个二进制数通过组合一样能表示1~num的每一数。那么就不会漏掉任何一种情况了。
模板代码

#include <iostream>
#include <cstring>
#include <cstdio>
#define INF 0x3f3f3f3f    // 用于完全背包初始化
#define MAX(a,b) a>b?a:b
using namespace std;
const int MAXN = 100050;
bool dp[MAXN];
int val[MAXN];            /// 硬币价值
int num[MAXN];            /// 硬币数量
int n, m, sum;
void ZeroPack(int v) {    /// 01背包
    for(int i = m; i >= v; i--) {
        dp[i] |=dp[i - v];
    }
}
void CompletePack(int v) {  /// 完全背包
    for(int i = v; i <= m; i++) {
        dp[i] |= dp[i - v];
    }
}
void MutiplePack(int v, int num) {///对于1-num的每个数,我们都可以用拆分成的二进制数将其组合起来。
    if(v * num >= m) {      /// 当这个硬币价值*数量大于m的时候可以看作硬币数量为无限个
        CompletePack(v);
    } else {
        int k = 1;
        while(k < num) {  /// 二进制优化
            ZeroPack(k * v);///每个都单独成一个01背包
            num -= k;
            k *= 2;
        }
        if(num>0)
        ZeroPack(num * v); /// 再算一下剩余的num单独成一个01背包
    }
}
int main() {
    while(~scanf("%d%d", &n, &m)) {
        if(n == 0 && m == 0) break;
        sum = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%d", &val[i]);
        }
        for(int i = 1; i <= n; i++) {
            scanf("%d", &num[i]);
        }
        memset(dp, 0, sizeof(dp)); /// 完全背包初始化
        dp[0] = 1;    /// 背包容量为0时符合情况
        for(int i = 1; i <= n; i++) {
            MutiplePack(val[i], num[i]);
        }
        for(int i = 1; i <= m; i++) {
            if(dp[i] > 0) sum++;    /// 当背包被定义说明情况存在
        }
        cout << sum << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值