算法基礎--貪心策略

本文主要做爲本身的學習筆記,並不具有過多的指導意義。

概述

貪心算法一般用來求解最優問題node

  1. 由局部最優解到總體最優解git

    經過不斷對局部最優進行操做,最終達到總體最優算法

  2. 無後效性swift

    後序操做,不會出現數據狀態的回滾數組

  3. 和DP(動態規劃)之間的聯繫bash

    不少貪心問題能夠經過DP進行求解dom


最優裝載問題

  1. 給出N個物體,第一個物體重量爲Mi
  2. 儘可能選擇最多的物品,總重不超過C

先將物品按照質量排序,而後依次放入每一個物品,直到總重量將超過C位置。ide

這裏依次將剩餘物品中質量最小的物品放入的過程,就是貪心的過程。學習


合併果子

一類總過程代價,取決於子過程代價的問題測試

  1. 有N堆果子,沒堆果子的數量爲Ai,每次能夠將兩堆果子合併,每次合併將消耗兩堆果子總數的體力。
  2. 求最小消耗的體力
  3. 1<N<10000

首先,若是咱們什麼都無論直接兩兩合併:總計消耗48點體力

而後,咱們嘗試排序後兩兩合併:總計消耗44點體力

最後,咱們嘗試只將當前全部數據中最小的兩個進行合併:總計消耗38點體力

解法

構建一個小根堆,每次從堆頂推出兩個元素合併。而且將合併都的元素追加進小根堆中便可。

具體證實的過程有必定難度,能夠參考哈夫曼編碼證實的過程。

以上的操做過程,也就是貪心的過程。他只保證單次合併所消耗的體力最優,而不在乎其餘的數據該如何合併。

堆結構每每用來解決貪心的問題。由於貪心問題每每須要一個明確的指標,最大值或者最小值。


項目利潤

輸入:

cost[]:每一個項目的花費

profits[]:每一個項目的利潤(純利潤)

k:最多能作k個項目

w:表示初始資金

輸出:

最後能夠得到的最大錢數

說明:一次只能作一個項目,且作完一個以後立刻就能得到收益,能夠支持作下一個項目

  1. costprofits中的元素依次合併成一個新的節點node:
public class Node {
    public var c :Int //項目花費
    public var p :Int //項目利潤
    
    public init(cost:Int,profit:Int) {
        self.c = cost
        self.p = profit
    }
}
複製代碼
  1. 準備一個以項目花費構建的小根堆

將全部node依次放入

  1. 準備一個以項目利潤構建的大根堆

貪心過程:

  1. 從小根堆中依次彈出堆頂元素,直到node.c>w(項目所需資金大於當前資金)

    具體代碼上,將小根堆數組removeFirst,而後將arr[0]與arr[arr.count-1]位置交換。讓小根堆對arr[0]位置元素向下調整便可。

  2. 將小根堆中彈出的元素放入大根堆中(大根堆中即爲當前可執行的項目)

    具體代碼上,將元素追加進大根堆數組末尾,並進行調整便可。

  3. 從大根堆中彈出堆頂元素,並將w += node.p(執行收益最大的項目,而且更新當前資金)

    具體代碼上與第一步相似

該貪心過程總計執行k次,每一次執行都只須要關心小根堆中最小值,與大根堆中最大值便可。最後的w即爲最大總資產。


會議安排

在優先的時間內安排數量最多的會議

作一張圖能夠直觀表示過程:

咱們將藍色表示爲待安排紅色表示爲已安排黑色表示爲不可安排

咱們能夠嘗試幾種不一樣的貪心策略

  1. 每次選擇持續時間最短的安排

顯然不可行

  1. 每次選擇開始時間最先的

顯然也不可行

  1. 每次選擇開始時間最先的而且持續時間最短的來安排

因而可知該方案是能夠行的

代碼也很簡單,只須要關心當前有效數據內開始時間晚於當前會議結束時間結束時間最先的一個數據便可。

func bestArrange(programs:[Program]) -> Int {
    program.sort("end")//根據program.end進行排序
    
    var res = 0
    var current = 0
    
    for p in programs {
        if p.current > current {  //開始時間晚於當前時間,不然做廢
            res += 1
            current = p.end //開會,當前時間變成會議結束時間
        }
    }
    return res
}
複製代碼

貪心策略的證實

貪心策略的數學證實一般很複雜,有能力能夠去翻閱

這裏推薦一種很方便的方式,對數器。

經過小樣本大樣本量的測試,證實貪心策略的正確性。

以排序算法的證實舉例

var checkOK = true
for i in 0..<10000 {
    var arr1 = generateRandomArray(size: 5, value: 20) //獲取一個長度爲10,最大值爲20的隨機數數組
    var arr2 = arr1 //數組在swift裏屬於值類型,賦值動做會自動copy
    let originalArr = arr1
    arr1.sort()//必定正確的算法
    radixSort(arr: &arr2, maxDigit: 2)
    if arr1 != arr2 {
        checkOK = false
        print(originalArr)
        print(arr2)
        break
    }
}

print(checkOK ? "比對成功":"比對失敗")
複製代碼

對於貪心問題,可能不必定存在一個必定正確的算法。那麼咱們徹底能夠不去比對結果是否一致,只要貪心策略的結果永遠優於默認順序得出的結果便可。

關於對數器的介紹能夠參閱另外一篇


參考資料

貪心算法

貪心算法3: 會議安排

左神牛課網算法課

相關文章
相關標籤/搜索