貪心算法(又稱貪婪算法)是指,在堆問題求解時,老是作出當前看來是最好的選擇。也就是說,不從總體最優上加以考慮,他所作出是在某種意義上的局部最優解。python
貪心算法並不保證會獲得最優解,可是在某些問題上貪心算法的解就是最優解。要會判斷一個問題可否用貪心算法來計算。算法
假設商店老闆須要找零n元錢,錢幣的面額有:100元、50元、20元、5元、1元,如何找零使得所需錢幣的數量最小?app
t = [100, 50, 20, 5, 1] def change(t, n): # n指的總金額 m = [0 for _ in range(len(t))] # 建立一個和t同樣長但全是0的列表 # 這裏假設t都是倒序排好的 for i, money in enumerate(t): m[i] = n // money # n整除money:376//100=3 n = n % money # n對money取餘:376%100=76 return m, n # 若是到最後找不開,n就是找不開的錢 print(change(t, 376)) # ([3, 1, 1, 1, 1], 0) 即:300+50+20+5+1 # 若是t = [100, 50, 20, 5],則有1塊錢找不開, print(change(t, 451)) # ([4, 1, 0, 0], 1)
一個小偷在某個商店發現有n個商品,第i個商品價值vi元,重wi千克。他但願拿走的價值儘可能高,但他的揹包最多隻能容納W千克的東西。他應該拿走哪些商品?函數
對於一個商品,小偷要麼把它完整拿走,要麼留下。不能只拿走一部分,或把一個商品拿走屢次。(商品爲金條)優化
對於一個商品,小偷能夠拿走其中任意一部分。(商品爲金砂)spa
答:貪心算法對分數揹包能夠獲得最優解,對0-1揹包得不到最優解,極可能揹包都沒法裝滿。blog
兩種揹包問題都具備最優子結構性質。對0-1揹包問題,考慮重量不超過W而價值最高的裝包方案。若是咱們將商品j今後方案中刪除,則剩餘商品必須是重量不超過W-wj的價值最高的方案(小偷只能從不包括商品j的n-1個商品中選擇拿走哪些)。排序
雖然兩個問題類似,但咱們用貪心策略能夠求解揹包問題,而不能求解0-1揹包問題,爲了求解部分數揹包問題,咱們首先計算每一個商品的每磅價值vi/wi。遵循貪心策略,小偷首先儘可能多地拿走每磅價值最高的商品,若是該商品已所有拿走而揹包未裝滿,他繼續儘可能多地拿走每磅價值第二高的商品,依次類推,直到達到重量上限W。所以,經過將商品按每磅價值排序,貪心算法的時間運行時間是O(nlgn)。字符串
爲了說明貪心這一貪心策略對0-1揹包問題無效,考慮下圖所示的問題實例。此例包含3個商品和一個能容納50磅重量的揹包。商品1重10磅,價值60美圓。商品2重20磅,價值100美圓。商品3重30磅,價值120美圓。所以,商品1的每磅價值爲6美圓,高於商品2的每磅價值5美圓和商品3的每磅價值4美圓。所以,上述貪心策略會首先拿走商品1。可是,最優解應該是商品2和商品3,而留下商品1。拿走商品1的兩種方案都是次優的。it
可是,對於分數揹包問題,上述貪心策略首先拿走商品1,是能夠生成最優解的。拿走商品1的策略對0-1揹包問題無效是由於小偷沒法裝滿揹包,空閒空間下降了方案的有效每磅價值。在0-1揹包問題中,當咱們考慮是否將一個商品裝入揹包時,必須比較包含此商品的子問題的解與不包含它的子問題的解,而後才能作出選擇。這會致使大量的重疊子問題——動態規劃的標識。
def fractional_backpack(goods, w): """ 貪心算法——分數揹包 :param goods: 商品 :param w: 揹包容量 :return: """ m = [0 for _ in range(len(goods))] # [0, 0, 0] total_v = 0 # 總價值 for i, (price, weight) in enumerate(goods): if w >= weight: m[i] = 1 total_v += price w -= weight else: # 不足1 m[i] = w / weight total_v += m[i]*price w = 0 break return m, total_v print(fractional_backpack(goods, 50)) # ([1, 1, 0.6666666666666666], 240.0) 60+100+120*2/3=60+100+80=240
有n個非負整數,將其按照字符串拼接的方式拼接爲一個總體。如何拼接可使得獲得的整數最大?
例:32,94,128,1286,6,71能夠拼接出的最大整數爲94716321286128
from functools import cmp_to_key # 傳遞python2中sort的cmp函數 li = [32, 94, 128, 1286, 6, 71] def xy_cmp(x, y): if x+y < y+x: # 要讓大的在前面,這裏須要交換 return 1 elif x+y > y+x: return -1 else: return 0 def number_join(li): li = list(map(str, li)) # 轉換爲字符串:['32', '94', '128', '1286', '6', '71'] # 方法1:cmp函數 li.sort(key=cmp_to_key(xy_cmp)) # 傳遞進xy_cmp函數 # 方法2:使用快排、冒泡排序等 return "".join(li) print(number_join(li)) # 94716321286128
假設有n個活動,這些互動要佔用同一片場地,而場地在某時刻只能供一個活動使用。
每一個活動都有一個開始時間si和結束時間fi(題目中時間以整數表示),表示活動在[si, fi)(左閉右開)區間佔用場地。
問:安排哪些活動可以使該場地舉辦的活動個數最多?
貪心結論:最早結束的活動必定是最優解的一部分。
證實:假設a是全部活動中最早結束的活動,b是最優解中最早結束的活動。
activities = [(1, 4), (3, 5), (0, 6), (5, 7), (3, 9), (5, 9), (6, 10), (8, 11), (8, 12), (2, 14), (12, 16)] # 保證活動是按照結束時間排好序的(每次選擇最先結束的) activities.sort(key=lambda x: x[1]) # print(activities) def activity_selection(a): """ 活動選擇 :param a: 活動 :return: """ res = [a[0]] # 將最先結束的活動放入結果中 for i in range(1, len(a)): # 對列表進行遍歷(除去第一個) if a[i][0] >= res[-1][-1]: # 當前活動開始時間 大於等於 res最後一個活動的結束時間 所以不衝突 res.append(a[i]) return res print(activity_selection(activities)) # [(1, 4), (5, 7), (8, 11), (12, 16)]
都是最優化問題。大大提高了解決問題的速度,可是它也有一些優化問題沒法解決或獲得的不是最優解。
貪心算法存在的問題: