动态规划-换钱的方法数(python实现)

算法题:换钱的方法数

最近在看左程云的《程序员代码面试指南》,感觉不错,题都分了类,很方便有目的的刷题,书里的代码都是java实现的,刚好最近在学习python,就用python去练习一下。

1. 问题描述

给定数组arr,其value代表货币面额,货币可无限张使用,给定一个整数aim作为要找的钱数,求组成aim的方法数。
举例:
arr = [5, 10, 25, 1]
有6种方法,分别为 [3, 0, 0, 0], [1, 1, 0, 0], [0, 1, 0, 5], [1, 0, 0, 10], [2, 0, 0, 5], [0, 0, 0, 15]
返回6

2.解决方法

1)暴力递归:从arr[0]开始,尝试每一种面值不同张数。具体看源码
2)动态规划:首先确定了本题尝试是无后效性的,即现状态的返回值与怎么到达这个状态无关。然后确定可变参数:cur 和 rest,cur代表钱币面值数组位置,rest代表要找钱数,与01背包问题有一点类似,从下往上,从左往右的构造表(也可以从上往下,主要取决于对m[i][j]的定义,我喜欢定义m[i][j]为选择i~N,rest=j时的结果)。初始化这张表后,把表中数据计算完整。递归关系不易看出,但画出图后分析可以得出m[i][j]依赖于m[i+1]几乎一整行的数据,再加以判断,可以发现,其实m[i][j]与同一行的数据也有一定联系:m[i][j]就是m[i][j - arr[i]] 和 m[i+1][j] 的相加之和

3.代码实现

# 暴力递归
def minForm(arr, p, aim):
    if p == len(arr):
        return 1 if aim == 0 else 0
    
    res = 0
    i = 0
    while arr[p]*i <= aim:
        res += minForm(arr, p+1, aim-arr[p]*i)
        i += 1

    return res

# 动态规划
def minForms(arr, aim):

    m = [[0 for i in range(aim+1)] for j in range(len(arr))]
    # 初始化m表
    j = len(arr)
    i = 0
    while arr[j-1]*i <= aim:
        m[j-1][arr[j-1]*i] = 1 
        i += 1
    for i in range(j):
        m[i][0] = 1

    # complete the m form
    i = len(arr)-2
    while i >= 0:
        for j in range(1,aim+1):
            if j - arr[i] >= 0:
                m[i][j] += m[i][j-arr[i]]
            if i + 1 < len(arr):
                m[i][j] += m[i+1][j]
        i -= 1

    print('m表:')
    for item in m:
        print(item)
    return m[0][aim]

if __name__ == "__main__":
    arr = [5, 10, 25, 1]
    aim = 15
    print('暴力递归方法结果:{0}'.format(minForm(arr, 0, aim)))
    print('动态规划方法结果:{0}'.format(minForms(arr, aim)))
暴力递归方法结果:6
m表:
[1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
动态规划方法结果:6
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值