Cash Machine
Description
A Bank plans to install a machine for cash withdrawal. The machine is able to deliver appropriate @ bills for a requested cash amount. The machine uses exactly N distinct bill denominations, say Dk, k=1,N, and for each denomination Dk the machine has a supply of nk bills. For example,
N=3, n1=10, D1=100, n2=4, D2=50, n3=5, D3=10
means the machine has a supply of 10 bills of @100 each, 4 bills of @50 each, and 5 bills of @10 each.
Call cash the requested amount of cash the machine should deliver and write a program that computes the maximum amount of cash less than or equal to cash that can be effectively delivered according to the available bill supply of the machine.
Notes:
@ is the symbol of the currency delivered by the machine. For instance, @ may stand for dollar, euro, pound etc.
N=3, n1=10, D1=100, n2=4, D2=50, n3=5, D3=10
means the machine has a supply of 10 bills of @100 each, 4 bills of @50 each, and 5 bills of @10 each.
Call cash the requested amount of cash the machine should deliver and write a program that computes the maximum amount of cash less than or equal to cash that can be effectively delivered according to the available bill supply of the machine.
Notes:
@ is the symbol of the currency delivered by the machine. For instance, @ may stand for dollar, euro, pound etc.
Input
The program input is from standard input. Each data set in the input stands for a particular transaction and has the format:
cash N n1 D1 n2 D2 ... nN DN
where 0 <= cash <= 100000 is the amount of cash requested, 0 <=N <= 10 is the number of bill denominations and 0 <= nk <= 1000 is the number of available bills for the Dk denomination, 1 <= Dk <= 1000, k=1,N. White spaces can occur freely between the numbers in the input. The input data are correct.
cash N n1 D1 n2 D2 ... nN DN
where 0 <= cash <= 100000 is the amount of cash requested, 0 <=N <= 10 is the number of bill denominations and 0 <= nk <= 1000 is the number of available bills for the Dk denomination, 1 <= Dk <= 1000, k=1,N. White spaces can occur freely between the numbers in the input. The input data are correct.
Output
For each set of data the program prints the result to the standard output on a separate line as shown in the examples below.
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
Hint
The first data set designates a transaction where the amount of cash requested is @735. The machine contains 3 bill denominations: 4 bills of @125, 6 bills of @5, and 3 bills of @350. The machine can deliver the exact amount of requested cash.
In the second case the bill supply of the machine does not fit the exact amount of cash requested. The maximum cash that can be delivered is @630. Notice that there can be several possibilities to combine the bills in the machine for matching the delivered cash.
In the third case the machine is empty and no cash is delivered. In the fourth case the amount of cash requested is @0 and, therefore, the machine delivers no cash.
In the second case the bill supply of the machine does not fit the exact amount of cash requested. The maximum cash that can be delivered is @630. Notice that there can be several possibilities to combine the bills in the machine for matching the delivered cash.
In the third case the machine is empty and no cash is delivered. In the fourth case the amount of cash requested is @0 and, therefore, the machine delivers no cash.
题目大意
给你一个总钱数cash,N种货币,每种有n个,问这些货币最多能拼成多少钱,使之最接近cash。
解题思路
思路一、多重背包的模板题,推荐看背包九讲
本来用的基本方法,结果超时了,果然还是得用优化的。心塞
其实就是转化为价值和消耗相等的01背包,把每种物品分解为多种物品,然后使用01背包求解。基本思路是枚举k ,让k*原参数成为一个新的物品,这样1个物品就变成了n个物品。
事实上这种办法容易超时,需要优化
考虑这样一个问题,真的需要枚举每个k吗?
例如某种物品数量为13,那么可以分解为1.2.3.4.5……13,这样每个数都能达到,但是拿取1和3是不是相当于拿取4呢?同样 拿取6 7是不是直接相当于拿取13呢
于是我们希望优化系数,想到二进制的思想,2进制可以表示任意数,如果我们把13分解为 1 2 4 6,这样会发现,1~13中每一个数都能用其中任意一个或多个相加来表示。
至于k,我们规定 n-2^k+1>0,最后一个 系数 是 总数 - 满足条件的所有k。
思路二、枚举面值
还有一种思想是枚举面值,如果面值为j可以达到,那么面值为 j+k*cost[i] (k属于n[i])也能达到.解题过程中就记录更新最大的面值。
代码
思路一
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int cash,n,i,j,k;
int dp[100010],num[11000],cost[11000];
void zeroonepack(int cost)
{
int i;
for(i=cash;i>=cost;--i)
{
dp[i]= max(dp[i-cost]+cost,dp[i]);
}
}
void complete_pack(int cost)
{
int i;
for(i=cost;i<=cash;++i)
dp[i]=max(dp[i],dp[i-cost]+cost);
}
void multiple_pack(int n,int cost)
{
if(n*cost>=cash)//相当于这个物品有无限件,完全背包
complete_pack(cost);
else
{
int k=1;
while(k<n)
{
zeroonepack(cost*k);
n-=k;
k*=2;
}
zeroonepack(cost*n);
}
}
int main()
{
while(scanf("%d",&cash)!=EOF)
{
scanf("%d",&n);
for(i=0;i<n;++i)
scanf("%d%d",&num[i],&cost[i]);
if(!cash || !n) printf("0\n");
else
{
memset(dp,0,sizeof(dp));
for(i=0;i<n;++i)
{
multiple_pack(num[i],cost[i]);
}
printf("%d\n",dp[cash]);
}
}
return 0;
}
思路二
#include<cstdio>
#include <cstring>
using namespace std;
int cash,n,i,j,k,maxn=0;
int dp[100010],num[11000],cost[11000];
int main()
{
while(scanf("%d",&cash)!=EOF)
{
scanf("%d",&n);
for(i=0;i<n;++i)
scanf("%d%d",&num[i],&cost[i]);
if(!cash || !n) printf("0\n");
else
{
maxn=0;
memset(dp,0,sizeof(dp));
dp[0]=1;
for(i=0;i<n;++i)
{
for(j=maxn;j>=0;j--)
{
if(dp[j])
{
for(k=1;k<=num[i];++k)
{
int t=j+k*cost[i];
if(t<=cash)
{
dp[t]=1;
if(t>maxn)
maxn=t;
}
}
}
}
}
printf("%d\n",maxn);
}
}
return 0;
}