锁定老帖子 主题:Ruby每周一测 - 找零钱
精华帖 (4) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-04-09
Java版,对11个测试分别循环1000次,总共需要15ms.
public class Test { public static int[] change(int change, int[] coins) { // 初始化 硬币种类 if (coins == null) { int[] cs = { 25, 10, 5, 1 }; coins = cs; } // 硬币种类的个数 int count = coins.length - 1; // 优化 忽略用不到的大面额硬币 int start = 0; for (; start <= count; start++) { if (change >= coins[start]) { break; } } if (start != 0) { count -= start; int[] cs = new int[count + 1]; System.arraycopy(coins, start, cs, 0, count + 1); coins = cs; } // 得到结果 int[] ns = change(change, coins, count); // 将用不到的大面额的硬币的个数设置为0 if (start != 0) { count += start; int[] cs = new int[count + 1]; System.arraycopy(ns, 0, cs, start, ns.length); ns = cs; } return ns; } public static int[] change(int change, int[] coins, int count) { // 最少的硬币个数据 int min = change / coins[0]; if (min * coins[0] == change) { int[] ns = new int[count + 1]; ns[0] = min; return ns; } else { min++; } // 最多的硬币个数据 int max = change / coins[count] + 1; for (int current = min; current <= max; current++) { int[] r = new int[count + 1]; // 初始值 r[0] = current; do { // 进位 if (r[0] == 0) { int i = 1; for (; i < count; i++) { if (r[i] != 0) { break; } } r[i + 1]++; r[i - 1] = r[i] - 1; r[i] = 0; } else { r[0]--; r[1]++; } // 比较 int sum = 0; for (int i = 0; i <= count; i++) { sum += r[i] * coins[i]; } if (sum == change) { return r; } } while (r[count] != current); // 循环结束 } return null; } public static void main(String[] args) { long start = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) { eq(change(25, new int[] { 25 }), new int[] { 1 }); eq(change(10, new int[] { 10 }), new int[] { 1 }); eq(change(1, new int[] { 1 }), new int[] { 1 }); eq(change(5, new int[] { 3, 2 }), new int[] { 1, 1 }); eq(change(5, new int[] { 4, 3, 2 }), new int[] { 0, 1, 1 }); eq(change(39, new int[] { 25, 10 }), null); eq(change(14, new int[] { 10, 7, 1 }), new int[] { 0, 2, 0 }); eq(change(14, new int[] { 5, 3, 2 }), new int[] { 1, 3, 0 }); eq(change(12, new int[] { 22, 3, 2 }), new int[] { 0, 4, 0 }); eq(change(40, new int[] { 20, 19, 1 }), new int[] { 2, 0, 0 }); eq(change(427, new int[] { 10000, 1000, 10, 1 }), new int[] { 0, 0, 42, 7 }); } long end = System.currentTimeMillis(); System.out.println((end - start) + "ms"); } public static boolean eq(int[] a, int[] b) { boolean r; if (a == null) { r = b == null; } else { r = true; int m = a.length - 1; for (int i = 0; i <= m; i++) { if (a[i] != b[i]) { r = false; } } } return r; } } |
|
返回顶楼 | |
发表时间:2008-05-03
def make_change(amount, coins=[25, 10, 5, 1]) begin plan = [] for am in coins.min..amount do if coins.include?(am) then plan[am] = [am] else size = 999999 for a in coins.min..am/2 do if plan[a] and plan[am-a] then s = plan[a].length + plan[am-a].length if s < size then size = s plan[am] = plan[a] + plan[am-a] end end end end end plan[amount] rescue StandardError => bang nil end end 这个方法和QuakeWang翻译的第二种方法类似。其实动态规划一般不考虑用递归。递归用到桟,桟一般较小,当递归层次很深的时候有可能桟溢出。第二种方法是递推,简单来说,就是从最基本的情况出发。例如这里,递归函数是 M(k) = min{M(1)+M(k-1), M(2)+M(k-2), ..., M(k/2, n-k/2)} 递归的方法是从 k = amount 的情况开始考虑。而递推的方法是从 k = 1 开始考虑。所以递推是先计算 k = 1 时,M(k) = ?,然后 k = 2 时,M(k) = ?,一直到 k = amount,M(k) = ?。 递推法因为没有用桟,而是用堆作为储存空间,所以递推法一般较递归要好。递推法也更好理解。 |
|
返回顶楼 | |
发表时间:2008-05-07
class Array; def sum; inject( nil ) { |sum,x| sum ? sum+x : x }; end; end def clonearray(arr) b = [] for a in arr b << a end return b end def match(condidatesolution,avail,paymoney) @temp = 0 for i in 0..avail.size-1 @temp += condidatesolution[i] * avail[i] end return ( @temp ==paymoney) end def printarray(arr) print "[" for a in arr print a.to_s + "," end print "]" end def makechange2(paymoney,avail) # 相当于解方程 aW+bX+cY+dZ = paymoney # 要求: # 这里W,X,Y,Z = 10,7,5,1 # 要求找到所有的[a,b,c,d]的集合中 # a+b+c+d 是最小的作为解。 # # 最大范围的数组 [3,5,7,39] # 获得最大数组 coinmax @coinmax = [] @coincurr = [] for coinavail in avail @coinmax << paymoney/coinavail @coincurr << 0 end #printarray @coinmax #printarray @coincurr @size = @coincurr.size() # 循环遍历可能解空间,升位,直到 @coincurr[@size-1] > @coinmax[@size-1] @i =0 @mincoincount = 99999 while @coincurr[@size-1] <= @coinmax[@size-1] if match(@coincurr,avail,paymoney) print "a solution:" printarray @coincurr puts @coincount = @coincurr.sum if @coincount < @mincoincount @mincoincount = @coincount @finalsolution = clonearray(@coincurr) end end @first = @coincurr.shift @coincurr.insert(0,@first+1) #print "--" #printarray @coincurr for i in 0..(@size -2) if @coincurr[i] > @coinmax[i] @coincurr[i+1] = @coincurr[i+1] + @coincurr[i] / (@coinmax[i]+1) @coincurr[i] = @coincurr[i] % (@coinmax[i]+1) end end end print "finalsolution:" printarray @finalsolution end makechange2( 39,[10,7,5,1]) |
|
返回顶楼 | |
发表时间:2008-05-30
这个应该用递归比较好吧,还有就是零钱中必须要有1,不然有时候会出错。
def make_change(money,change_array) alist=change_array.sort{|x,y| x <=> y } alength=change_array.length change=0 for i in (0..alength-1) if money>=alist[i] change=alist[i] else break end end if money>0 puts change make_change(money-change,change_array) end end list=[25,10,5,1] make_change(23,list)#10 10 1 1 1 make_change(55,list)#25 25 5 make_change(1,list)# 1 make_change(39,list)#25 10 1 1 1 1 |
|
返回顶楼 | |
发表时间:2008-05-30
没看大家的做了一个
做完之后 再看大家的 自己想的很不全面啊 谢谢各位 受教了 |
|
返回顶楼 | |
发表时间:2008-05-30
seairy 写道 def make_change(amount, coins = [25, 10, 5, 1]) change = [] coins.each do |coin| (amount / coin).times do change << coin amount -= coin end end change if amount.zero? end p make_change(39) #[25, 10, 1, 1, 1, 1] p make_change(14, [10, 7, 1]) #[10, 1, 1, 1, 1] p make_change(14, [5, 3, 2]) #nil p make_change(12, [22, 3, 2]) #[3, 3, 3, 3] |
|
返回顶楼 | |