HDU_2844 Coins(DP)

来源:互联网 发布:c语言产生随机数代码 编辑:程序博客网 时间:2024/06/05 16:55

Coins

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


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 101 2 4 2 1 12 51 4 2 10 0
 

Sample Output
84
 
题解:这是一道多重背包问题。
多重背包问题:
有N种物品和体积为V的背包,物品i的体积为v[i],价值为w[i],数目为n[i],求怎样装使背包内物品价值最大。
对于这道题来说,这个多重背包的v与w是相等的 ,我们把1~m之间的每一个钱数当做一个背包,我们只要求在1~m之间有多少个背包满足最大价值就好。
附上两种不同的解法:
方法1:O(nm)。
#include <iostream>#include <stdio.h>#include <stdlib.h>#include <cstring>#include <algorithm>#include <limits.h>#define MAX_A 100020#define MAX_N 110using namespace std;//dp数组存储加和得到j时第i种能剩多少个int dp[MAX_A];int n,m,num;int a[MAX_N];//价值int c[MAX_N];//数目int main(){    while( scanf("%d%d",&n,&m) != EOF )    {        if( n == 0 && m == 0 )            return 0;        for( int i = 0; i < n; i++ )            scanf("%d",&a[i]);        for( int i = 0; i < n; i++ )            scanf("%d",&c[i]);        //将初始值设为-1表示不能取到j        memset(dp,-1,sizeof(dp));        dp[0]=0;num=0;        for( int i = 0; i < n; i++ )        {            for( int j = 0; j <= m; j++ )            {                //j已经可以取到                if( dp[j] >= 0 )                    dp[j]=c[i];                //两种情况j取不到                else if( j < a[i] || dp[j-a[i]] <= 0 )                    dp[j]=-1;                //j可以取到,更新剩余个数                else                    dp[j]=dp[j-a[i]]-1;            }        }        //有一种方法类似,但是要稍微快一些        //dp数组存储加和得到j时第i种用多少个        /*        int vis[MAX_A];        memset(dp,0,sizeof(dp));        memset(vis,0,sizeof(vis));        for( int i = 0; i < n; i++ )        {            memset(dp,0,sizeof(dp));            for( int j = a[i]; j <= m; j++ )            {                if( vis[j-a[i] && !vis[j] )                {                    if( dp[j-a[i]] < c[i] )                    {                        vis[j]=1;                        num++;                        dp[j]=dp[j-a[i]]+1;                    }                }            }        }        */        for( int i = 1; i <= m; i++ )        {            //剩余个数>=0表示最终能取到            if( dp[i] >= 0 )                num++;        }        printf("%d\n",num);    }    return 0;}
方法2:O(n∑logci)。我们可以把多重背包转换为我们学过的01背包或者完全背包。把第i种物品换成n[i]件01背包中的物品,则得到了物品数为∑n[i]的01背包问题,直接求解,复杂度仍然是O(V ∑n[i])。但是我们期望将它转化为01背包问题之后能够像完全背包一样降低复杂度。仍然考虑二进制的思想,我们考虑把第i种物品换成若干件物品,使得原问题中第i种物品可取的每种策略取0..n[i]件均能等价于取若干件代换以后的物品
多重背包代码精华:
void multiply(int cost,int n){    if(cost*n>=v) complete(cost);    else{        int k=1;        while(k<n){        bag01(cost*k);        n-=k;        k*=2;        }        bag01(n*cost);    }}

//dp数组存储背包为j时的总价值最大值int main(){    while( scanf("%d%d",&n,&m) && (n||m) )    {        for( int i = 0; i < n; i++ )            scanf("%d",&a[i]);        for( int i = 0; i < n; i++ )            scanf("%d",&c[i]);        //初始值设为0        memset(dp,0,sizeof(dp));num=0;        for( int i = 0; i < n; i++ )        {            //如果物品i取不完,化为完全背包            if( a[i]*c[i] > m )            {                for( int j = a[i]; j <= m; j++ )                    dp[j]=max(dp[j],dp[j-a[i]]+a[i]);            }            else            {                //二进制优化,将物品i分为1,2,4...(和等于i的总个数),转化为01背包                int k;                int amount=c[i];                for( k = 1; k < amount; amount-=k,k*=2 )                {                    for( int j = m; j >= k*a[i]; j-- )                        dp[j]=max(dp[j],dp[j-k*a[i]]+k*a[i]);                }                for( int j = m; j >= amount*a[i]; j-- )                    dp[j]=max(dp[j],dp[j-amount*a[i]]+amount*a[i]);            }        }        for( int i = 1; i <= m; i++ )        {            if( i == dp[i] )                num++;        }        printf("%d\n",num);    }}
0 0
原创粉丝点击