我尋思着,不能只會暴力求解和動態規劃吧,所以看了一下回溯法。
本文以找零問題爲例,首先使用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]來表示其各個硬幣的選取情況。
回溯的精髓只有動手做了才清楚,也是清楚的那一瞬間讓我很舒爽。奧利給,衝。