hdu2844 多重背包+二进制优化(多重背包的完全背包优化解法)

                                                         Coins

                                                 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
                                                                  Total Submission(s): 12746    Accepted Submission(s): 5116


Problem Description
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
 
题意:给你n种钱,每种钱有Cn张,问你最多能组成多少种钱数。
思路:好开心的一道题,昨晚就在想多重背包的二进制优化这个问题,结果今天就做到了。o(^▽^)o。这个本来是一个非常简单的多重背包问题,看一下if (dp[ i - money[j] ] == 1)  dp[i] = 1;但是他给的数据太大了,1e5的钱,有100种钱,每种钱1000张,这样的话复杂度就变成了1e10,显然会TLE,所以我们用二进制对钱的数量进行优化,那1000张,可以化为2*log2(1000),不到20,这样的话,复杂度就到了1e8,勉强能过。。。不过我在做出来以后看他们的题解,发现有的人用了另一种方法,他把这个题当作完全背包设了一个use[i]的数组,代表 i 元用了多少张这种钱,每放一种钱,他就memset一遍,这样的话,他的复杂度只有1e7。这很ACM。
贴一下二进制优化多重背包的代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int a[105][15];
int dp[100050];
int value[105];
void bt(int re, int n){
    int number = 0;
    int k = 1;
    while(n >= k){
        number++;
        n -= k;
        a[re][number] = k;
        k = k << 1;
    }
    while(n > 0){
        number++;
        while(k > n)
            k = k >> 1;
        n -= k;
        a[re][number] = k;
    }
    a[re][0] = number;
}
int main(){
    int n,m,temp;
    while(scanf("%d%d",&n,&m) && n+m){
        memset(dp,0,sizeof(dp));
        for(int i = 0; i < n; i++)
            scanf("%d",&value[i]);
        for(int i = 0; i < n; i++){
            scanf("%d",&temp);
            bt(i,temp);
        }
        dp[0] = 1;
        for(int i = 0; i < n; i++)
            for(int j = 1; j <= a[i][0]; j++)
                for(int k = m; k >= a[i][j]*value[i]; k--)
                    if(dp[k-a[i][j]*value[i]] == 1)dp[k] = 1;
        int number = 0;
        for(int i = 1; i <= m; i++)
            if(dp[i])number++;
        cout << number <<endl;
    }
}

完全背包加use数组的解法:
#include <stdio.h>
#include <string.h>
 
bool dp[100010];
int use[100010];//i元钱时某种钱用的次数
int n,m;
int val[110],num[110];
 
void solve()
{
    memset(dp,0,sizeof(dp));
    dp[0] = 1;
    int count = 0;
    for(int i = 1; i <= n; i++)
    {
        memset(use,0,sizeof(use));  //每次初始化第i种钱用了0次
        for(int j = val[i]; j <= m; j++)//顺序枚举钱数
        {
            if(dp[j-val[i]] && !dp[j] && use[j-val[i]] < num[i])
            {
                dp[j] = 1;
                use[j] = use[ j-val[i] ]+1;//到达j元用的i种钱的次数是到达 j-val[i]元用的次数加1
                count++;
            }
        }
    }
    printf(%d
,count);
}
int main()
{
    while(~scanf(%d %d,&n,&m))
    {
        if(n == 0 && m == 0)
            break;
 
        for(int i = 1; i <= n; i++)
            scanf(%d,&val[i]);
 
        for(int i = 1; i <= n; i++)
            scanf(%d,&num[i]);
 
        solve();
 
    }
    return 0;
 
}
</string.h></stdio.h>

感谢这个博主,贴一下他的这篇博客网址:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值