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.
InputThe 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. OutputFor each set of data the program prints the result to the standard output on a separate line as shown in the examples below. Sample Input735 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 Output735
630
0
0
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.
分析:这个题实际上就是一个多重背包问题,就是将n个体积为w价值为v物品在放入次数为k的限制条件下放入体积为v的背包里面,使得总的价值最大。
背包问题的话,关键是要理解好0-1背包,其他类型的背包问题都是通过转化为0-1背包来实现的,那么多重背包的思想就是将每种物品总的价值(单价乘以个数)分别放置在几个盒子里面,使得任意属于总价值的一个数都可以通过这几个的盒子的组合来组成这个数,每一个都这样处理,就达到每一个盒子只会用到一次,因为每一个盒子都是原来物品的不同组合,也就相当于每个物品用了多次,这样就把原来的多重背包转化为0-1背包问题。
那么关键问题就是如何转化呢?即如何分解为多个盒子呢?
我们先来看一个小问题,由此引入。
假设有1000个珍珠,要求把他们分解放在9个盒子里面,使得任何>0&&<=1000的数都可以通过这些盒子组合出来。那么我们如何解决这个问题呢?思考一下。
思路是这样的,我们知道任意一个数都可以写成二进制的形式,那么我们把九个盒子分别对应于二进制数的每一位的,九个盒子分别是:1,2,4,8.。。。。489,最后一个盒子的话就用总的1000减去前面所能组成的最大数。这样任意一个数都可以用这些盒子组合起来啦。
那么根据这种思想,我们在分解每一个物品的时候也就是这样分解的,具体上代码吧!
import java.util.*;
public class Main {
static Scanner in = new Scanner(System.in);
static int cash,n;
static int[] num = new int[15];
static int[] value = new int[15];
static int[] dp = new int[100000];
static int[] v = new int[100000];
static int[] w = new int[100000];
public static void main(String[] args) {
while(in.hasNext()){
Arrays.fill(dp,0);
cash = in.nextInt();
n = in.nextInt();
int cnt = 0;
for(int i =1;i<= n;i++){
num[i] = in.nextInt();//个数
value[i] = in.nextInt();//价值
}
//对输入进行预处理,即进行盒子分解
for(int i =1;i<= n;i++){
//其实和二进制一样的思想,每次为value*(2^(j-1))
for(int j = 1; j <= num[i]; j *= 2){
v[++cnt] = value[i]*j;
w[cnt] = value[i]*j;
num[i] -= j;
}
//最后一个盒子
if(num[i]!=0){
v[++cnt] = value[i]*num[i];
w[cnt] = value[i]*num[i];
}
}
//0-1背包处理
for(int i = 1;i<= cnt;i++){
//System.out.println(w[i]+" "+v[i]);
for(int j = cash;j >= w[i];j--){
dp[j]=Math.max(dp[j], dp[j-w[i]]+v[i]);
//System.out.print("("+j+" "+dp[j]+") ");
}
//System.out.println();
}
System.out.println(dp[cash]);
}
}
}