貪心算法


貪心算法(又稱貪婪算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的是在某種意義上的局部最優解

貪心算法不是對所有問題都能得到整體最優解,關鍵是貪心策略的選擇,選擇的貪心策略必須具備無后效性,即某個狀態以前的過程不會影響以后的狀態,只與當前狀態有關。

 

思想

貪心算法的基本思路是從問題的某一個初始解出發一步一步地進行,根據某個優化測度,每一步都要確保能獲得局部最優解。每一步只考慮一個數據,他的選取應該滿足局部優化的條件。若下一個數據和部分最優解連在一起不再是可行解時,就不把該數據添加到部分解中,直到把所有數據枚舉完,或者不能再添加算法停止

過程

  1. 建立數學模型來描述問題;

  2. 把求解的問題分成若干個子問題;

  3. 對每一子問題求解,得到子問題的局部最優解;

  4. 把子問題的解局部最優解合成原來解問題的一個解。                            

假設山洞中有 n 種寶物,每種寶物有一定重量 w 和相應的價值 v,毛驢運載能力有限,
只能運走 m 重量的寶物,一種寶物只能拿一樣,寶物可以分割。那么怎么才能使毛驢運走寶
物的價值最大呢?
我們可以嘗試貪心策略:
(1)每次挑選價值最大的寶物裝入背包,得到的結果是否最優?
(2)每次挑選重量最小的寶物裝入,能否得到最優解?
(3)每次選取單位重量價值最大的寶物,能否使價值最高?
思考一下,如果選價值最大的寶物,但重量非常大,也是不行的,因為運載能力是有限
的,所以第 1 種策略舍棄;如果選重量最小的物品裝入,那么其價值不一定高,所以不能在
總重限制的情況下保證價值最大,第 2 種策略舍棄;而第 3 種是每次選取單位重量價值最大
的寶物,也就是說每次選擇性價比(價值/重量)最高的寶物,如果可以達到運載重量 m,

那么一定能得到價值最大。
因此采用第 3 種貪心策略,每次從剩下的寶物中選擇性價比最高的寶物。

算法設計
(1)數據結構及初始化。將 n 種寶物的重量和價值存儲在結構體 three(包含重量、價
值、性價比 3 個成員)中,同時求出每種寶物的性價比也存儲在對應的結構體 three 中,將
其按照性價比從高到低排序。采用 sum 來存儲毛驢能夠運走的最大價值,初始化為 0。
(2)根據貪心策略,按照性價比從大到小選取寶物,直到達到毛驢的運載能力。每次選
擇性價比高的物品,判斷是否小於 m(毛驢運載能力),如果小於 m,則放入,sum(已放入
物品的價值)加上當前寶物的價值,m 減去放入寶物的重量;如果不小於 m,則取該寶物的
一部分 m * p[i],m=0,程序結束。m 減少到 0,則 sum 得到最大值。

完美圖解
假設現在有一批寶物,價值和重量如表 2-3 所示,毛驢運載能力 m=30,那么怎么裝入
最大價值的物品?
表 2-3  寶物清單
寶物 i  1  2  3  4  5  6  7  8  9  10
重量 w[i]  4  2  9  5  5  8  5  4  5  5
價值 v[i]  3  8  18  6  8  20  5  6  7  15
(1)因為貪心策略是每次選擇性價比(價值/重量)高的寶物,可以按照性價比降序排
序,排序后如表 2-4 所示。
表 2-4  排序后寶物清單
寶物 i  2  10  6  3  5  8  9  4  7  1
重量 w[i]  2  5  8  9  5  4  5  5  5  4
價值 v[i]  8  15  20  18  8  6  7  6  5  3
性價比 p[i]  4  3  2.5  2  1.6  1.5  1.4  1.2  1  0.75
(2)按照貪心策略,每次選擇性價比高的寶物放入:
第 1 次選擇寶物 2,剩余容量 30−2=28,目前裝入最大價值為 8。
第 2 次選擇寶物 10,剩余容量 28−5=23,目前裝入最大價值為 8+15=23。
第 3 次選擇寶物 6,剩余容量 23−8=15,目前裝入最大價值為 23+20=43。
第 4 次選擇寶物 3,剩余容量 15−9=6,目前裝入最大價值為 43+18=61。

第 5 次選擇寶物 5,剩余容量 6−5=1,目前裝入最大價值為 61+8=69。
第 6 次選擇寶物 8,發現上次處理完時剩余容量為 1,而 8 號寶物重量為 4,無法全部
放入,那么可以采用部分裝入的形式,裝入 1 個重量單位,因為 8 號寶物的單位重量價值為
1.5,因此放入價值 1×1.5=1.5,你也可以認為裝入了 8 號寶物的 1/4,目前裝入最大價值為
69+1.5=70.5,剩余容量為 0。
(3)構造最優解
把這些放入的寶物序號組合在一起,就得到了最優解(2,10,6,3,5,8),其中最后
一個寶物為部分裝入(裝了 8 號財寶的 1/4),能夠裝入寶物的最大價值為 70.5。

偽代碼詳解
(1)數據結構定義
根據算法設計中的數據結構,我們首先定義一個結構體 three:
struct three{
double w; //每種寶物的重量
double v; //每種寶物的價值
double p; //每種寶物的性價比(價值/重量)

(2)性價比排序
我們可以利用 C++中的排序函數 sort(見附錄 B),對寶物的性價比從大到小(非遞增)
排序。要使用此函數需引入頭文件:
#include <algorithm>
語法描述為:
sort(begin, end)// 參數 begin 和 end 表示一個范圍,分別為待排序數組的首地址和尾地址
在本例中我們采用結構體形式存儲,按結構體中的一個字段,即按性價比排序。如果不
使用自定義比較函數,那么 sort 函數排序時不知道按哪一項的值排序,因此采用自定義比較
函數的辦法實現寶物性價比的降序排序:
bool cmp(three a,three b)//比較函數按照寶物性價比降序排列
{
return a.p > b.p; //指明按照寶物性價比降序排列
}
sort(s, s+n, cmp); //前兩個參數分別為待排序數組的首地址和尾地址
//最后一個參數 compare 表示比較的類型
(3)貪心算法求解
在性價比排序的基礎上,進行貪心算法運算。如果剩余容量比當前寶物的重量大,則可以放入,剩余容量減去當前寶物的重量,已放入物品的價值加上當前寶物的價值。如果剩余
容量比當前寶物的重量小,表示不可以全部放入,可以切割下來一部分(正好是剩余容量),
然后令剩余容量乘以當前物品的單位重量價值,已放入物品的價值加上該價值,即為能放入
寶物的最大價值。
for(int i = 0;i < n;i++)//按照排好的順序,執行貪心策略
{
if( m > s[i].w )//如果寶物的重量小於毛驢剩下的運載能力,即剩余容量
{
m -= s[i].w;
sum += s[i].v;
}
else //如果寶物的重量大於毛驢剩下的承載能力
{
sum += m 乘以 s[i].p; //進行寶物切割,切割一部分(m 重量),正好達到驢子承重
break;
}
}

#include<iostream>
#include<algorithm>
using namespace std;
const int M=1000005;
struct three{
    double w;//每個寶物的重量
    double v;//每個寶物的價值
    double p;//性價比
}s[M];
bool cmp(three a,three b)
{
    return a.p>b.p;//根據寶物的單位價值從大到小排序
}
int main()
{
    int n;//n 表示有 n 個寶物
    double m ;//m 表示毛驢的承載能力
    cout<<"請輸入寶物數量 n 及毛驢的承載能力 m :"<<endl;
    cin>>n>>m;
    cout<<"請輸入每個寶物的重量和價值,用空格分開: "<<endl;
    for(int i=0;i<n;i++)
    {
        cin>>s[i].w>>s[i].v;
        s[i].p=s[i].v/s[i].w;//每個寶物單位價值
    }
    sort(s,s+n,cmp);
    double sum=0.0;// sum 表示貪心記錄運走寶物的價值之和
    for(int i=0;i<n;i++)//按照排好的順序貪心
    {
        if( m>s[i].w )//如果寶物的重量小於毛驢剩下的承載能力
    {
        m-=s[i].w;
        sum+=s[i].v;
    }
        else//如果寶物的重量大於毛驢剩下的承載能力
        {
            sum+=m * s[i].p;//部分裝入
            break;
        }
    }
    cout<<"裝入寶物的最大價值 Maximum value="<<sum<<endl;
    return 0;
}

附:本題是個DP問題,用貪心法並不一定可以求得最優解,以后了解了動態規划算法后本題就有了新的解法

貪心算法示例點擊打開鏈接

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM