您的位置:首页 > 其它

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

2015-06-21 17:51 435 查看
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: