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

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 1000010
int 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;
}

你可能感兴趣的:(POJ 1276 Cash Machine(多重背包问题))