多重背包二进制写法

for (j = 1; j * a[i] <= m && j <= x; x -= j, j *= 2)
	b[++tot] = j * a[i];
if (x) b[++tot] = x * a[i];

x为物品价格为a[i]个数,方法就是把x拆为多个二进制,从而能组成1…n的所有数字
例如
x=10100
1
10
100
1000
101
这些分出来的数字和一定是x

题目 Coins

题意

n n 种钱币,每种有 C [ i ] C[i] 个,面值为 A [ i ] A[i] ,求用这些钱币在 [ 1 , m ] [1,m] 中所能组成的面值个数
多重背包问题,先把每个钱币的个数用二进制拆分拆,就变成01背包问题

#include <cstdio>
#include <iostream>
#define lb(x) (x & -x)
#define jh(a, b) a ^= b, b ^= a, a ^= b
using namespace std;
inline void read(int &x){
    x = 0; int f = 1; char ch = getchar();
    while (!(ch >= '0' && ch <= '9')){if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}
inline void Max(int &x, int y){if (y > x) x = y;}
const int N = 1e7 + 10;
int n, m, a[N], b[N], tot, ans;
bool f[N];
int main(){
    while (cin >> n >> m){
        if (!(n | m)) break;
        tot = ans = 0;
        for (int i = 0; i <= m; i++) f[i] = false;
        for (int i = 1; i <= n; i++) read(a[i]);
        for (int i = 1; i <= n; i++){
            int x, j; read(x);
            for (j = 1; j * a[i] <= m && j <= x; x -= j, j *= 2)
                b[++tot] = j * a[i];
            if (x) b[++tot] = x * a[i];
        }
        f[0] = true;
        for (int i = 1; i <= tot; i++)
            for (int j = m; j >= b[i]; j--)
                f[j] |= f[j - b[i]];
        for (int i = 1; i <= m; i++) if (f[i]) ans++;
        printf("%d\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44627639/article/details/88320244