多重背包 证明二进制拆分n[i]得到的数能够组成0~n[i]

什么是二进制拆分?

 一个数n拆成小于它的所有二的次方的和(指数递增的)加上剩下一个数。实际上就是将这个数的二进制的每一位选或者不选就可以组成一个范围在0~n的数

  举个例子:13可以拆成 2^0 、 2^1 、 2^2、和 6(6是剩下的那个数),也就是拆分成 1 2 4 6,那么通过选择这4个数的就可以表示0~13的任意数。

如:
0:一个都不选。
1:选1;
2:选2;
3:选1和2;
4: 选4;
5:选1和4;
6:选2和4;
7:选1和2和4;
8:选2和6;
9:选1和2和6;
10: 选4和6;
11:选1和4和6;
12:选2和4和6;
13:全选;

以下给出个人的证明其中把n直接用来表示n[i]

证明:

①证0 ~ 2^k-1( 2^(k+1)- 1 <= n)可以被表示

由幂级数(等比数列)求和可得:

2^0 + 2^1 + 2^2 + …… +2^k = 2^(k+1)-1
2^0 + 2^1 + 2^2 + …… +2^k < 2^(k+1)

所以一个k+1位的二进制数至少可以拆分为 k个 二的次方(指数递增),那么不难得到k位二进制数已经可以被这些拆出来的数表示了。
所以现在0~2^(k+1) - 1( 2^(k+1)-1 <= n < 2^(k+2) - 1 )的十进制数已经可以被表示了

②下证 2^k ~ n可以被这些数表示

明显拆分得到的所有数就可以组成n,
那么组成n的那些数减去1(除了2^0不选,其他都选)就可以得到 n-1
组成n的那些数减去2(除了2^1不选,其他都选)就可以得到 n-2
组成n的那些数减去3(除了2^0 和 2^不选,其他的都选)就可以得到 n-3
……………………
……………………
……………………
对于n-x 有
  2^(k+1) - 1 <= n-x < 2^(k+2) - 1
  2^(k+1)-1 <= n < 2^(k+2) - 1

上式子联立得: 0<=x < 2^(k+1)也就是 0 <= x <= 2(k+1) -1;
通过①结论可得x可被这些拆分出来的数表示。
 也就是先将所有数( 2^0 、 2^1 、 2^2 + …… +2^k 和拆分剩下的那个数)都选,那么n就可以被表示出来,那么n-x可以通过从所有数中把组成X的数移出来表示x的数就就可以得到n-x

所以命题得证

应用:二进制拆分可以用在完全背包上,用来降低时间复杂度,把1~n枚举改为对拆分的这些数进行枚举,时间复杂度从O(n)降低到o(logn)

贴代码

非函数调用

#include <iostream>
using namespace std;
const int N = 110;
int n,V;
int w[N];
int v[N];
int s[N];
int dp[N];
int max(int i,int j){
    return i > j?i:j;
}

int main(){
    cin >> n >> V;

    for(int i = 1;i <= n;++i)
        cin >> v[i] >> w[i] >> s[i];
        
        for(int i = 1;i <= n;i++){
            if(s[i]*v[i]>=V){//但物品个数足够装满整个背包时,使用完全背包更快
                for(int j = v[i];j <= V;j++){
                    dp[j] = max(dp[j],dp[j-v[i]]+w[i]);
                }
            }
            else//物品个数不够,则用01背包 因为物品个数不够装满整个包 所以 s[i] < V/v[i]
            for(int key = 1;s[i] > 0;s[i]-=key,key*=2){//选择key个 每次Key 个i物品进行01背包
                if(s[i]>=key){
                    for(int j = V;j >= key*v[i];j--){
                        dp[j] = max(dp[j],dp[j-key*v[i]] + key*w[i]);
                    }
                }else//二进制拆分剩下的数
                    for(int j =V;j >= s[i]*v[i];j--)
                        dp[j] = max(dp[j],dp[j-s[i]*v[i]] + s[i]*w[i]);
                
            }
        }
    int result = 0;
    for(int j = 0;j <= V;j++){
        result < dp[j] ? result = dp[j]:result = result;
    }
    cout << result;
    return 0;
}

调用函数

#include <iostream>
using namespace std;
const int N = 110;
int n,V;
int w[N];
int v[N];
int s[N];
int dp[N];
int max(int i,int j){
    return i > j?i:j;
}
void zeroonepack(int i){

    for(int key =1;s[i]>0;s[i]-=key,key<<=1){
        if(s[i]>=key)
            for(int j = V;j >= key*v[i];j--)
                dp[j] = max(dp[j],dp[j - v[i]*key] + w[i] *key);
        else
            for(int j = V;j >= s[i]*v[i];j--)
                dp[j] = max(dp[j],dp[j - v[i]*s[i]] + w[i] *s[i]);
    }
}
void completpack(int i){
    for(int j = v[i];j <= V;j++)
        dp[j] = max(dp[j],dp[j-v[i]]+w[i]);
}
int main(){
    cin >> n >> V;

    for(int i = 1;i <= n;++i)
        cin >> v[i] >> w[i] >> s[i];
        
        for(int i = 1;i <= n;i++){
            if(s[i]*v[i]>=V){//物品个数不受限
                completpack(i);
            }
            else{//s[i] <V/v[i]; 物品个数受限
               zeroonepack(i);
            }
                
        }
        
    int result = 0;
    for(int j = 0;j <= V;j++){
        result < dp[j] ? result = dp[j]:result = result;
    }
    cout << result;
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很抱歉,由于我是一名语言模型AI,我并不具备直接运行代码的能力。但是,我可以为您提供一些关于多重背包二进制优化输入c代码的信息和建议。 多重背包问题是一种经典的动态规划问题,二进制优化是对传统多重背包算法的一种优化手段,可以大幅提升算法性能。 以下是一个可能的多重背包二进制优化输入c代码的基本框架: ```c #include <stdio.h> #define MAXN 1005 int N; // 物品量 int W; // 背包总容量 int w[MAXN]; // 每个物品的重量 int v[MAXN]; // 每个物品的价值 int f[MAXN][MAXN]; int max(int a, int b) { return a > b ? a : b; } int main() { scanf("%d %d", &N, &W); for (int i = 1; i <= N; i++) { scanf("%d %d", &w[i], &v[i]); } // 以下为二进制优化部分 int n = 1; for (int i = 1; i <= N; i++) { int k = 1; while (k <= v[i]) { w[n] = k * w[i]; v[n++] = k * v[i]; v[i] -= k; k *= 2; } if (v[i] > 0) { w[n] = v[i] * w[i]; v[n++] = v[i] * v[i]; } } N = n - 1; // 多重背包部分 for (int i = 1; i <= N; i++) { for (int j = W; j >= w[i]; j--) { for (int k = 1; k * w[i] <= j; k++) { f[j] = max(f[j], f[j - k * w[i]] + k * v[i]); } } } printf("%d\n", f[W]); return 0; } ``` 以上代码实现了基于二进制优化的多重背包问题,通过对每个物品按其价值进行二进制拆分,并将其转化为多个物品,以便于后续的多重背包求解。 需要注意的是,该算法的时间复杂度为 $O(NV\log V)$,其中 $V$ 为所有物品的总价值。如果您需要更高效的算法实现,可以尝试其他优化技巧,如单调队列优化、有界背包优化等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值