POJ 1276 Cash Machine(多重背包问题)

来源:互联网 发布:手机制作结婚照软件 编辑:程序博客网 时间:2024/04/30 12:25

Description
有各种不同面值的货币,每种面值的货币有不同的数量,请找出利用这些货币可以凑成的最接近且小于等于给定的数字cash的金额
Input
多组用例,以文件尾结束输入。
每组用例以cash N n1 D1 n2 D2 ..nN DN形式输入
(0<=cash<=100000,0<=N<=10,0<=ni<=1000,1<=nk<=1000)
cash为货币和上限,N为货币种类,ni和Di分别为每种货币的面额和数量
Output
对于每组用例,输出利用给出货币可以凑成的最接近的小于等于cash的金额
Sample Input
735 3 4 125 6 5 3 350
633 4 500 30 6 100 1 5 0 1
735 0
0 3 10 100 10 50 10 10
Sample Output
735
630
0
0
Solution
多重背包问题
将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为1,2,4,…,2^k−1,n[i]−2^k+1,且k是满足n[i]−2^k+1>0的最大整数。例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品。分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示。这样就将一个多重背包问题转化为一个01背包问题了
Code

#include<cstdio>#include<cstring>#include<iostream>using namespace std;#define INF (1<<29)#define maxn 1000010int dp[maxn];int n[230],w[230];int b[11]={1,2,4,8,16,32,64,128,256,512,1024};int ans,N;int main(){    while(scanf("%d%d",&ans,&N)!=EOF)    {        memset(n,0,sizeof(n));        memset(w,0,sizeof(w));         memset(dp,0,sizeof(dp));        int count=1,i,j,k;        for(int i=1;i<=N;i++)        {            int nn,ww;            scanf("%d%d",&nn,&ww);            if(nn!=0)            {                for(j=10;j>=0;j--)                    if(nn-b[j]+1>0)                        break;                for(k=0;k<j;k++)                {                    n[count]=b[k];                    w[count]=ww*b[k];                    count++;                }                n[count]=nn-b[j]+1;                w[count]=ww*(nn-b[j]+1);                count++;            }        }        count--;        for(i=1;i<=count;i++)            for(j=ans;j>=w[i];j--)                dp[j]=max(dp[j],dp[j-w[i]]+w[i]);        printf("%d\n",dp[ans]);    }    return 0;}
0 0