较难的动态规划问题——付款问题,面值任意,可找零
2012-04-12 16:15
337 查看
在超市买东西经常会遇到付款、找钱的问题。
一般来说,我们的货币面值也就 100, 50, 20, 10, 5, 1, 0.5, 0.1 这几种,基本都是单位1或者是5的倍数。如果问:最少要付多少张钞票?这个问题是比较容易的,只要从大往小付款就可以了。
进一步如果可以让服务员找零,并且找零的钞票数量也算在总钞票数里,让总钞票数最少。比如付98元,只需要付100元找1+1元,一共三张,而不需要50+20+10+5+1+1+1 七张。这个问题稍有难度,可以思考一下。
由于今天在一个python网站上做题,没仔细看题目。以至于花了一天时间做出来了这个问题:
给定一组任意面值的钞票,以及要付款的钱数,在可以找零钱的条件下(找零的钞票张数也算在使用的钞票张数里),求最少一共使用多少张钞票?
找零所使用的钞票面值,也在给定的面值里。比如说,给定面值[100, 99],付款50元,最佳方案是 支付99元50张,找零100元49张,一共用了10张。
————————————————以下剧透答案————————————————————————————————
反反复复想错了好几次,最后发现一开始写的代码基本上是对的。难点是roof上限的确定,以及能否支付的判断
想清楚鸟,给定的面值,所能支付的最小定价,是由它们的公共最大公约数决定的!这样一来,能否支付以及支付张数的上限(roof)也就可以提前知道了,方便后面的搜索。想明白了还真简单啊。
剩余的工作留给未来~~:
既然已知给定面值所能组成的最小价格,那么通过搜索就能把“基础面额”算出来,然后用“基础面额”来做下一步:用正向搜索来支付价格,不用考虑找零。
这样的话主循环就会简单的多啦。
有空搞出来吧~
————————————————以下是之前犯的错误,心路历程————————————————————————
一开始我觉得这道题非常难,在“装包问题”的基础上(见之前的博文《DP为王》),不断的正向、反向双向搜索,非常复杂,而且似乎结果还没弄对。
后来在思考这样一个子问题时,有了灵感。
子问题:给定一系列钞票和价格,理论上能否支付?
不能支付是为什么呢?是因为有可能无论怎么找零都无法支付,比如给你的面值都是2、10、20之类的偶数,而付款是奇数,你就被坑了 :)
之前认为是错的地方整个都不对{
(之前有一个错误想法,正写这个博客时候纠正过来了。以前我认为 价格 能被“最小面值”整除,此价格就能被支付,否则不行。其中“最小面值” 是能够通过加减组合得到的最小面值。比如上面举的例子[100,99],由于100-99=1,所以实际上所有正整数都可以被支付。
而我在算[7, 11, 13]时候发现不对。[7,11,13]能否支付的最小面额是2(此处仍然是错的,可以付1元,只需要付7+7,找13元即可),但是它却可以用来支付7、11、13、9、15等等。可以试试看。
}
()
一般来说,我们的货币面值也就 100, 50, 20, 10, 5, 1, 0.5, 0.1 这几种,基本都是单位1或者是5的倍数。如果问:最少要付多少张钞票?这个问题是比较容易的,只要从大往小付款就可以了。
进一步如果可以让服务员找零,并且找零的钞票数量也算在总钞票数里,让总钞票数最少。比如付98元,只需要付100元找1+1元,一共三张,而不需要50+20+10+5+1+1+1 七张。这个问题稍有难度,可以思考一下。
由于今天在一个python网站上做题,没仔细看题目。以至于花了一天时间做出来了这个问题:
给定一组任意面值的钞票,以及要付款的钱数,在可以找零钱的条件下(找零的钞票张数也算在使用的钞票张数里),求最少一共使用多少张钞票?
找零所使用的钞票面值,也在给定的面值里。比如说,给定面值[100, 99],付款50元,最佳方案是 支付99元50张,找零100元49张,一共用了10张。
————————————————以下剧透答案————————————————————————————————
反反复复想错了好几次,最后发现一开始写的代码基本上是对的。难点是roof上限的确定,以及能否支付的判断
想清楚鸟,给定的面值,所能支付的最小定价,是由它们的公共最大公约数决定的!这样一来,能否支付以及支付张数的上限(roof)也就可以提前知道了,方便后面的搜索。想明白了还真简单啊。
def gcd(a,b): while b!=0: a,b = b,a%b return a def checkio(data): 'return minimal amount of used coins and notes.' d = {} l, dest = data # if l has float, amplify the numbers to integer l_dot = [len(str(i)) -1 - str(i).find('.') for i in l if int(i)!=i] if len(l_dot) != 0: l = [i*10**max(l_dot) for i in l] dest *= 10**max(l_dot) # use gcd, we can calc the minimal value that can be paid. mini = reduce(gcd, l) if dest % mini != 0: # This value can't be paid. return 0 roof = dest / mini for i in l: d[i] = 1 cur = 0 while True: cur += 1 for cur1 in xrange(1, cur+1): for i in sorted(d.keys()): if d[i] >= roof: continue for j in l: if i + j == cur1 and ( not d.has_key(cur1) or d[cur1]>d[i]+1 ): d[cur1] = d[i] + 1 if cur1 == dest and ( d[cur1]<roof ): roof = d[cur1] for cur2 in xrange(cur, 0, -1): for i in sorted(d.keys(), reverse=True): if d[i] >= roof: continue for j in l: if i - j == cur2 and ( not d.has_key(cur2) or d[cur2]>d[i]+1 ): d[cur2] = d[i] + 1 if cur2 == dest and ( d[cur2]<roof ): roof = d[cur2] if d.has_key(cur) and d[cur]>=roof: break print d print d[dest] return d[dest] if __name__ == '__main__': #You give 100$ and get in change 1$ assert checkio([[100,50,20,10,5,1],99]) == 2, 'First' #You can't give change #assert checkio([[50,20,5],41]) == 0, 'Second' # You give 2 times 20 and 1 or 50 and 1 and get 10 in change. #Other combinations are posible but you will use more then 3 notes. assert checkio([[50,20,10,5,1,0.5],41])==3, 'Third' assert checkio([[50, 20, 10, 5, 1], 78]) == 5 , 'Fourth' assert checkio([[1], 78]) == 78 , 'Fifth' checkio([[7,11,13], 1]) checkio([[100,98], 1])
剩余的工作留给未来~~:
既然已知给定面值所能组成的最小价格,那么通过搜索就能把“基础面额”算出来,然后用“基础面额”来做下一步:用正向搜索来支付价格,不用考虑找零。
这样的话主循环就会简单的多啦。
有空搞出来吧~
————————————————以下是之前犯的错误,心路历程————————————————————————
一开始我觉得这道题非常难,在“装包问题”的基础上(见之前的博文《DP为王》),不断的正向、反向双向搜索,非常复杂,而且似乎结果还没弄对。
后来在思考这样一个子问题时,有了灵感。
子问题:给定一系列钞票和价格,理论上能否支付?
不能支付是为什么呢?是因为有可能无论怎么找零都无法支付,比如给你的面值都是2、10、20之类的偶数,而付款是奇数,你就被坑了 :)
之前认为是错的地方整个都不对{
(之前有一个错误想法,正写这个博客时候纠正过来了。以前我认为 价格 能被“最小面值”整除,此价格就能被支付,否则不行。其中“最小面值” 是能够通过加减组合得到的最小面值。比如上面举的例子[100,99],由于100-99=1,所以实际上所有正整数都可以被支付。
而我在算[7, 11, 13]时候发现不对。[7,11,13]能否支付的最小面额是2(此处仍然是错的,可以付1元,只需要付7+7,找13元即可),但是它却可以用来支付7、11、13、9、15等等。可以试试看。
}
()
相关文章推荐
- Java动态规划之硬币找零问题实现代码
- 动态规划之硬币找零问题
- 动态规划在求解硬币问题中的应用(JAVA)--币制最大化、找零问题、硬币收集问题
- 从自动贩卖机找零看Python中的动态规划问题
- 动态规划之硬币面值组合问题
- 动态规划:最少硬币找零问题、01背包问题、完全背包问题
- 动态规划之找零问题
- 双层动态规划_吃土豆问题
- 『算法设计_伪代码』动态规划问题
- 动态规划(背包问题)java实现
- 动态规划--走台阶问题
- 动态规划之最少硬币凑钱问题
- 动态规划之01背包问题(Knapsacks Problem)
- (SPFA处理有后效性的动态规划问题) bzoj 3875
- 动态规划----最长上升子序列问题
- 动态规划问题系统---edit-distance
- 动态规划-硬币问题
- 0/1背包问题动态规划详解之一
- 动态规划解决01背包问题