有一组砝码,重量互不相等,分别为m1、m2、m3……mn;它们可取的最大数量分别为x1、x2、x3……xn。
现要用这些砝码去称物体的重量,问能称出多少种不同的重量。
现在给你两个正整数列表w和n, 列表w中的第i个元素w[i]表示第i个砝码的重量,列表n的第i个元素n[i]表示砝码i的最大数量。 i从0开始
请你输出不同重量的种数。
如:w=[1,2], n=[2,1], 则输出5(分析:共有五种重量:0,1,2,3,4)
解题
参考智力题砝码称重问题,有一个这样的样例:砝码重量:[1,3,9],个数:[1,1,1]可以称出1到13内的质量
然后自己死活写不出来,网上整理了砝码称重的智力题,又发现说这个是背包问题,就做了背包问题相关题目
PythonTip解题报告中给了下面的程序:
根据输出结果可以看出:本题的砝码称重,砝码必须放在同一侧,砝码一侧,重物一侧。在上面的测试环境中可以通过,网上看到的砝码称重大部分就是这样的称重
上面程序的原理:
定义集合set:表示能够称出的重物重量
set初始化:所有砝码的重量
遍历所有种类的砝码:
遍历该种类砝码的数量:
可以称出新的重物质量 = 集合内的值 减去 砝码的重量*砝码的个数
循环结束后集合内的值就是答案了
根据果壳 中的讲解,可以发现上面的过程没有 -a 的情况,只有 0 a的情况,也就是说砝码必须都放在同一侧
初始集合值是0:
length_n=len(w) S=set([]) S.add(0) for i in range(length_n): temp_S=S.copy() for s in temp_S: j=1 while j<=n[i]: S.add(s+j*w[i]) j+=1 #print S print len(S)
Java
public int backPack2(int[]A,int []B){ int total = 0; for(int i=0;i<A.length;i++) total +=A[i]*B[i]; TreeSet<Integer> set = new TreeSet<Integer>(); set.add(total); for(int i=0;i<A.length;i++){ Object[] setCopy = set.toArray(); for(Object s:setCopy){ int j = 1; while(j<=B[i]){ set.add(((Integer)s-j*A[i])); j++; } } } for(Integer s:set){ System.out.print(s+" "); } return set.size(); }
下面考虑砝码可以放在天平的不同侧的情况
参考:http://www.tuicool.com/articles/yqYjM3
讲解见注释
/** * A[i] 第i个砝码的重量 * B[i] 第i个砝码的数量 * @param A * @param B * @return */ public int backPack3(int[]A,int []B){ int total = 0; for(int i=0;i<A.length;i++) total +=A[i]*B[i]; boolean dp[] = new boolean[total+1]; // dp[i] = true 表示可以称出质量为 i 的重物 dp[0] = true; for(int i=0;i<A.length;i++){// 砝码重量 for(int j = 0;j<=B[i];j++){// 对 第 i个砝码遍历其种类 for(int s=total;s>=A[i];s--){ // 砝码的总重量开始遍历,为什么? total -- A[i] 的质量是否能够称出 能否称出体现着 s-A[i]能否称出 if(dp[s - A[i]]) // dp[s - A[i]] = true 就是可以称出 s - A[i]的重物,也就是可以称出 s - A[i] + A[i] 的重物 dp[s] = true; } } } int count = 0; for(int i = 0;i<total+1;i++){ if(dp[i]){ System.out.print(i+" "); count++; } } return count; }