HDU-2844(多重背包中二进制优化)

Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. One day Hibix opened purse and found there were some coins. He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(without change) and he known the price would not more than m.But he didn't know the exact price of the watch.

You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins.
Input
The input contains several test cases. The first line of each test case contains two integers n(1 ≤ n ≤ 100),m(m ≤ 100000).The second line contains 2n integers, denoting A1,A2,A3...An,C1,C2,C3...Cn (1 ≤ Ai ≤ 100000,1 ≤ Ci ≤ 1000). The last test case is followed by two zeros.
Output
For each test case output the answer on a single line.
Sample Input
3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0
Sample Output
8

4

  这是一道多重背包问题,可以讲硬币的价值给变成重量与价值都为同一值的背包,然后就转化为了一道多重背包问题。不过如果直接按照多重背包来做,就会超时的,所以必须在多重背包的基础上加上二进制优化。

不用二进制优化的多重背包,就是暴力的去把同样类型的背包去当做0,1背包来做。不过其中可以加上二进制优化。

二进制优化基于数论的一个知识,任意的一个整数中的任意的一个书都可以由,所有不大于这个整数的的2的n方的数组合而成。

例如 17 = 1 + 2 + 4 + 8 + 2; 17 中的任意数都可以由1,2,4,8,2组合而出.

再例如:53 = 1 + 2 + 4 + 8 + 16 + 20; 也同样如此,所以可以基于此对此进行优化。将k个背包拆成0.1背包的形式。

#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;
int n, m;
int v[105];
int num[105];
int dp[100010];
int main()
{
   while(true) {
    scanf("%d%d", &n, &m);
    if(n == 0 && m == 0) break;
    for (int i = 0; i < n; i++)
      scanf("%d", &v[i]);
    for (int i = 0; i < n; i++)
      scanf("%d", &num[i]);
    memset(dp, 0, sizeof(dp));
    for (int i = 0; i < n; i++) {
      int cnt = num[i];
      for (int k = 1; k <= cnt; k <<= 1) {
        for (int j = m; j >= k * v[i]; j--) {
          dp[j] = max(dp[j], dp[j - k*v[i]] + k * v[i]);
        }
        cnt -= k;
      }
      if(cnt) for (int j = m; j >= cnt * v[i]; j--)
        dp[j] = max(dp[j], dp[j-cnt*v[i]] + cnt * v[i]);
      }
      int sum = 0;
      for (int i = 1; i <= m; i++) {
        if(dp[i] == i) sum++;
      }
      printf("%d\n", sum);
  }
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值