回溯法求解硬币找零问题的Python实现与个人理解

我寻思着,不能只会暴力求解和动态规划吧,所以看了一下回溯法。

本文以找零问题为例,首先使用python进行实现,然后想讲讲个人对于这个算法的想法。

 1 代码

def backtracking_exchange(amount, denominations):
    """
    >>> backtracking_exchange(56, [20, 10, 5, 1])
    [2, 1, 1, 1]
    >>> backtracking_exchange(12, [9, 6, 5, 1])
    [0, 2, 0, 0]
    """
    def is_equal_amount(part):
        # part表示值大小
        return True if sum(part) == amount else False

    def options(max_coin, part):
        # 获取可选集合
        res = []
        for coin in denominations:
            if coin <= max_coin and sum(part+[coin]) <= amount:  # 减少遍历的条件,1是选择小于等于前一个硬币的大小,2是选择后总大小小于等于目标值
                res += [coin]
        return res

    def solutions(max_coin, part=[]):
        if is_equal_amount(part):
            return [part]  # 如果等于了就返回part
        else:
            res = [] # 结果集合
            for o in options(max_coin, part):
                res += solutions(o, part + [o])
            return res  # 如果不符合,返回的是[], 不会影响总体,可以理解为回溯。

    res = sorted(solutions(denominations[0]), key=lambda x: len(x))[0]  # 获取结果
    # 以下简单加工成目标结果所需结构
    buff_dic = {coin: 0 for coin in denominations}
    for coin in res:
        buff_dic[coin] += 1  # 计数
    return [buff_dic[coin] for coin in denominations]


if __name__=='__main__':
    import doctest  # 测试模块
    doctest.testmod(verbose=True)

2 个人想法

    因为看到了八皇后问题(8-Queen Puzzle)而学习了回溯法,看了一些同学的讲解,说这个算法有dfs的思想,可以用迭代求解。大多是宏观上的讲解,讲真,看完一圈我没看懂具体应用是个啥玩意,不知道怎么从特殊到一般。所以想在这讲讲自己的看法,也算是对大家的补充。

    首先,从解题核心思想上看,回溯法解法使用了dfs的思想,并且加入了剪枝技术(一开始以为好高大上,后来才明白就是提前结束的深度检索的过程)。通过迭代的方法可以很好的进行dfs遍历,一条路走到黑。走不下去的路,以例题为例,就是当前获取的金额大于目标金额amount,我就不走了,减少了遍历的路径。然后通过return []达到回溯的目的。

   其次,从分解问题的角度上看,回溯法解题,总共可以分两不分。第一部分是迭代部分,包括选取迭代终止标志:is_equal_amount(part),以及迭代的最小子集:res += solutions(o, part + [o])。第二部分,下一步的所有选项集合,即候选集,通过函数solutions(max_coin, part=[])实现,这一步就好比八皇后问题中放了一个皇后之后,获取下一步有哪些可选步骤。

    本题可以通过两种方式获取硬币情况,第一个就是上面写的方法,直接将面值记录,最后再计算个数,第二种我没有写,可以直接使用等长列表[0,0,0,0]来表示其各个硬币的选取情况。

  回溯的精髓只有动手做了才清楚,也是清楚的那一瞬间让我很舒爽。奥利给,冲。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值