Multiple knapsacks prove that the number obtained by binary splitting n[i] can form 0~n[i]

What is binary split?

 A number n is divided into the sum (exponentially increasing) of all two powers less than it plus the remaining number. In fact, each bit of the binary number of this number can be selected or not selected to form a number in the range of 0~n

  For example: 13 can be split into 2^0, 2^1, 2^2, and 6 (6 is the remaining number), that is, split into 1 2 4 6, then by selecting these 4 numbers It can represent any number from 0 to 13.

Such as:
0: select none.
1: Choose 1;
2: Choose 2;
3: Choose 1 and 2;
4: Choose 4;
5: Choose 1 and 4;
6: Choose 2 and 4;
7: Choose 1 and 2 and 4;
8: Choose 2 and 6;
9: choose 1 and 2 and 6;
10: choose 4 and 6;
11: choose 1 and 4 and 6;
12: choose 2 and 4 and 6;
13: choose all;

The following is a personal proof, where n is directly used to represent n[i]

prove:

① Prove that 0 ~ 2^k-1 (2^(k+1)-1 <= n) can be expressed

From the sum of the power series (geometric sequence), we can get:

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

So a k+1 binary number can be split into at least k powers of two (exponentially increasing), then it is not difficult to get that k-bit binary numbers can already be represented by these split numbers.
So now 0~2^(k+1)-1 (2^(k+1)-1 <= n <2^(k+2)-1) decimal numbers can be represented

②The following proof 2^k ~ n can be represented by these numbers

Obviously, all the numbers obtained by splitting can form n,
then those numbers that make up n are subtracted by 1 (except for 2^0 not selected, all others are selected), and the
numbers that make up n by n-1 minus 2 (except 2 If you don’t select ^1, select all others) and you can get the number of n-2
composed of n minus 3 (except for 2^0 and 2^ not selected, all others are selected) and you can get n-3
…………… ………
……………………
………………
For nx there are
  2^(k+1)-1 <= nx <2^(k+2)-1
  2^(k+1 ) -1 <= n <2^(k+2)-1

The above equations are combined to get: 0<=x <2^(k+1), which is 0 <= x <= 2(k+1) -1;
through the conclusion of ① we can get the number x can be divided by these Said.
 That is, first select all the numbers (2^0, 2^1, 2^2 + …… +2^k and the remaining number after the split), then n can be expressed, then nx can be represented by In all the numbers, you can get nx by moving the numbers that make up X out to represent the number of x.

So the proposition is proved

Application: Binary splitting can be used on a complete backpack to reduce the time complexity. The 1~n enumeration is changed to enumerate the split numbers, and the time complexity is reduced from O(n) to o(logn). )

Post code

Non-function call

#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;
}

Call functions

#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;
}

Guess you like

Origin blog.csdn.net/RunningBeef/article/details/111031018