回溯法求解硬幣找零問題的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]來表示其各個硬幣的選取情況。

  回溯的精髓只有動手做了才清楚,也是清楚的那一瞬間讓我很舒爽。奧利給,衝。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章