日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

五大常用算法之贪心算法

發(fā)布時(shí)間:2023/12/10 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 五大常用算法之贪心算法 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

注:因本文撰寫(xiě)的時(shí)候參考了大量資料和博文,出處在此就不全部列出了,非常感謝前輩們分享的學(xué)習(xí)心得

有人歸納了計(jì)算機(jī)的五大常用算法,它們是貪心算法,動(dòng)態(tài)規(guī)劃算法,分治算法,回溯算法以及分支限界算法。雖然不知道為何要將這五個(gè)算法歸為最常用的算法,但是毫無(wú)疑問(wèn),這五個(gè)算法是有很多應(yīng)用場(chǎng)景的,最優(yōu)化問(wèn)題大多可以利用這些算法解決 ,接下來(lái)介紹的就是貪心算法。

一 貪心算法的概念?

所謂貪心算法,是指在對(duì)問(wèn)題求解時(shí),總是做出在當(dāng)前看來(lái)是最好的選擇。也就是說(shuō),不從整體最優(yōu)上加以考慮,他所做出的僅是在某種意義上的局部最優(yōu)解。舉個(gè)例子就很清楚了:現(xiàn)在有一個(gè)能裝4斤蘋(píng)果的袋子,蘋(píng)果有兩種,一種3斤一個(gè),一種2斤一個(gè),怎么裝才能得到最多蘋(píng)果?當(dāng)然如果是我們?nèi)丝紤]的話(huà)當(dāng)然是拿兩個(gè)2斤的蘋(píng)果,就剛好裝滿(mǎn)了,但是如果按貪心算法拿的話(huà),首先就要把最重的那個(gè)3斤的蘋(píng)果拿下(局部最優(yōu)),但其實(shí)拿2個(gè)2斤的蘋(píng)果能剛好裝4斤,所以并沒(méi)有得到最多蘋(píng)果(整體最優(yōu))。

二 什么問(wèn)題適合用貪心算法

對(duì)于一個(gè)詳細(xì)的問(wèn)題,怎么知道是否可用貪心算法解此問(wèn)題,以及是否能得到問(wèn)題的最優(yōu)解? 我們能夠依據(jù)貪心法的倆個(gè)重要的性質(zhì)去證明:貪心選擇性質(zhì)和最優(yōu)子結(jié)構(gòu)性質(zhì)。

貪心選擇:什么叫貪心選擇?從字義上就是貪心也就是目光短線。貪圖眼前利益。在算法中就是僅僅依據(jù)當(dāng)前已有的信息就做出選擇,并且以后都不會(huì)改變這次選擇。(這是和動(dòng)態(tài)規(guī)劃法的主要差別)所以對(duì)于一個(gè)詳細(xì)問(wèn)題。要確定它是否具有貪心選擇性質(zhì),必須證明每做一步貪心選擇是否終于導(dǎo)致問(wèn)題的總體最優(yōu)解

最優(yōu)子結(jié)構(gòu):當(dāng)一個(gè)問(wèn)題的最優(yōu)解包括其子問(wèn)題的最優(yōu)解時(shí),稱(chēng)此問(wèn)題具有最優(yōu)子結(jié)構(gòu)性質(zhì)。這個(gè)性質(zhì)和動(dòng)態(tài)規(guī)劃法的一樣,是可用動(dòng)態(tài)規(guī)劃算法或貪心算法求解的關(guān)鍵特征。

區(qū)分動(dòng)態(tài)規(guī)劃:動(dòng)態(tài)規(guī)劃算法通常以自底向上的方式解各子問(wèn)題,是遞歸過(guò)程;貪心算法則通常以自頂向下的方式進(jìn)行,以迭代的方式作出相繼的貪心選擇,每作一次貪心選擇就將所求問(wèn)題簡(jiǎn)化為規(guī)模更小的子問(wèn)題。以遍歷二叉樹(shù)為例:貪心算法是從上到下僅僅進(jìn)行深度搜索,也就是說(shuō)它從根節(jié)點(diǎn)一口氣走到底,它的代價(jià)取決于子問(wèn)題的數(shù)目,也就是樹(shù)的高度,每次在當(dāng)前問(wèn)題的狀態(tài)上作出的選擇都是1,不進(jìn)行廣度搜索,所以它得到的解不一定是最優(yōu)解,很可能是近似最優(yōu)解;而動(dòng)態(tài)規(guī)劃算法在最優(yōu)子結(jié)構(gòu)的前提下,從樹(shù)的葉子節(jié)點(diǎn)開(kāi)始向上進(jìn)行搜索,而且每一步都依據(jù)葉子節(jié)點(diǎn)的當(dāng)前問(wèn)題的狀況作出選擇,從而作出最優(yōu)決策,所以它的代價(jià)是子問(wèn)題的個(gè)數(shù)和可選擇的數(shù)目,它得到的解一定是最優(yōu)解。

三 貪心算法的求解過(guò)程?

一般求解過(guò)程:

  • 候選集合(C):通過(guò)一個(gè)候選集合C作為問(wèn)題的可能解
  • 解集合(S): 每完畢一次貪心選擇,將一個(gè)解放入S
  • 解決函數(shù)(solve): 檢查解集合S是否構(gòu)成問(wèn)題的完整解
  • 選擇函數(shù)(select): 即貪心策略,選擇出最有希望構(gòu)成問(wèn)題的解的對(duì)象
  • 可行函數(shù)(availabe): 檢查解集合中增加一個(gè)候選對(duì)象是否可行
  • 代碼實(shí)現(xiàn):

    Greedy(C) //C是問(wèn)題的輸入集合即候選集合

    {

    ??? S={ }; //初始解集合為空集

    ??? while(not solve(S)) //集合S沒(méi)有構(gòu)成問(wèn)題的一個(gè)解

    ??? {

    ?????? x=select(C); //在候選集合C中做貪心選擇

    ?????? if availabe(S, x) //推斷集合S中增加x后的解是否可行

    ????????? S=S+{x};

    ????????? C=C-{x};

    ??? }

    ??? return S;

    }

    四 經(jīng)典案例分析?

    [活動(dòng)選擇問(wèn)題]這是《算法導(dǎo)論》上的一個(gè)案例,也是一個(gè)非常經(jīng)典的問(wèn)題:

    學(xué)校只有一個(gè)教室,下面表格i代表活動(dòng)的編號(hào),s代表活動(dòng)開(kāi)始時(shí)間,f代表活動(dòng)結(jié)束時(shí)間,現(xiàn)在問(wèn)題是怎么安排這些活動(dòng)使得盡量多的活動(dòng)能不沖突的舉行?

    i

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    s[i]

    1

    3

    0

    5

    3

    5

    6

    8

    8

    2

    12

    f[i]

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    表 1.1 活動(dòng)時(shí)間表

    用動(dòng)態(tài)規(guī)劃的話(huà)較麻煩:假設(shè)我已經(jīng)知道第k個(gè)活動(dòng)是活動(dòng)序列之一,那么又把1到k和k到11看做兩個(gè)子問(wèn)題繼續(xù)分下去。

    用貪心算法的話(huà)很簡(jiǎn)單:活動(dòng)越早結(jié)束,剩余的時(shí)間越多,那就找到最早結(jié)束的那個(gè)活動(dòng),找到后在剩下的活動(dòng)中再找最早結(jié)束的,最后輸出找到的總數(shù)。

    貪心選擇示例:

    圖 1.1 貪心選擇圖

    流程圖示例:

    圖 1.2 思路流程圖

    代碼:

    #include<bits/stdc++.h> using namespace std;
    int N;
    struct Act {int start;int end; }act[100010];
    bool cmp(Act a,Act b) { return a.end<b.end; }
    int greedy_activity_selector() { int num=1,i=1; for(int j=2;j<=N;j++) { if(act[j].start>=act[i].end) { i=j; num++; } } return num; }int main() { int t;scanf("%d",&t);while(t--){scanf("%d",&N);for(int i=1;i<=N;i++){scanf("%lld %lld",&act[i].start,&act[i].end);}act[0].start=-1;act[0].end=-1;sort(act+1,act+N+1,cmp); int res=greedy_activity_selector();cout<<res<<endl; } }

    雖然貪心算法的思想簡(jiǎn)單,但是貪心法不保證能得到問(wèn)題的最優(yōu)解,如果得不到最優(yōu)解,那就不是我們想要的東西了,比如常見(jiàn)的背包問(wèn)題就不能使用貪心算法,下面舉個(gè)例子:

    [0-1背包問(wèn)題]有一個(gè)背包,背包容量是M=150。有7個(gè)物品,有著各自的重量和價(jià)值。現(xiàn)要求盡可能讓裝入背包中的物品總價(jià)值最大,但不能超過(guò)總?cè)萘俊?/p>

    物品 A ?B ?C ?D ?E ?F ?G

    重量 35 30 60 50 40 10 25

    價(jià)值 10 40 30 50 35 40 30

    分析:

    目標(biāo)函數(shù):∑pi最大

    約束條件是裝入的物品總重量不超過(guò)背包容量:∑wi<=M( M=150)

    3種貪心策略:

    (1)每次挑選價(jià)值最大的物品裝入背包,是否能得到最優(yōu)解?

    (2)每次挑選所占重量最小的物品裝入背包,是否能得到最優(yōu)解?

    (3)每次選取單位重量?jī)r(jià)值最大的物品裝入背包,是否能得到最優(yōu)解?

    證明:

    一般來(lái)說(shuō),貪心算法的證明圍繞著:整個(gè)問(wèn)題的最優(yōu)解一定由在貪心策略中存在的子問(wèn)題的最優(yōu)解得來(lái)的。

    對(duì)于例題中的3種貪心策略,都是無(wú)法被證明的,解釋如下:

    (1)貪心策略:選取價(jià)值最大者。反例:

    W=30

    物品:A ?B ?C

    重量:28 12 12

    價(jià)值:30 20 20

    根據(jù)策略,首先選取物品A,接下來(lái)就無(wú)法再選取了,可是,選取B、C則更好。

    (2)貪心策略:選取重量最小者。它的反例與第一種策略的反例差不多。

    (3)貪心策略:選取單位重量?jī)r(jià)值最大者。反例:

    W=30

    物品:A ?B ?C

    重量:28 20 10

    價(jià)值:28 20 10

    根據(jù)策略,三種物品單位重量?jī)r(jià)值一樣,程序無(wú)法依據(jù)現(xiàn)有策略作出判斷,如果選擇物品A,則答案錯(cuò)誤。

    但如果現(xiàn)在考慮這樣一種背包問(wèn)題:在選擇物品i裝入背包時(shí),可以選擇物品的一部分,而不一定要全部裝入背包,即可以分割物品,這時(shí)便可以使用貪心算法求解了。計(jì)算每種物品的單位重量?jī)r(jià)值作為貪心選擇的依據(jù)指標(biāo),選擇單位重量?jī)r(jià)值最高的物品,將盡可能多的該物品裝入背包,依此策略一直地進(jìn)行下去,直到背包裝滿(mǎn)為止。在之前的0-1背包問(wèn)題中三種貪心策略之所以不能得到最優(yōu)解的原因是貪心選擇無(wú)法保證最終能將背包裝滿(mǎn),部分閑置的背包空間使每公斤背包空間的價(jià)值降低了。以HDUOJ-1009為例:

    [老鼠交易問(wèn)題]老鼠準(zhǔn)備了M磅的貓糧,準(zhǔn)備與守衛(wèi)他最喜歡的食物JavaBean的倉(cāng)庫(kù)的貓交易。該倉(cāng)庫(kù)有N個(gè)房間。第i個(gè)房間包含J[i]磅的JavaBeans并且需要F[i]磅的貓糧。老鼠不需要為房間里的所有JavaBeans進(jìn)行交易,相反,如果他支付F[i]*a%磅的貓糧,他會(huì)得到J[i]*a%磅的JavaBeans。現(xiàn)在要求出老鼠能得到的最多的JavaBean的數(shù)量。

    分析:這道題其實(shí)就是一個(gè)0-1背包問(wèn)題,只不過(guò)物品可以被分割的放進(jìn)背包中,所以我們的貪心策略當(dāng)然是選取單位數(shù)量?jī)r(jià)值最大者放進(jìn)背包。

    代碼:

    #include<bits/stdc++.h> using namespace std;
    struct jb {double j,f,avg;//用于保存每個(gè)房間的信息 };
    bool cmp(jb x,jb y) {return x.avg<y.avg;//定義排序規(guī)則,即均值小的排在前面 }
    int main() {double n;int m;while(cin>>n>>m&&n!=-1){jb s[1001];for(int i=0;i<m;i++){cin>>s[i].j>>s[i].f;s[i].avg=s[i].j/s[i].f;}sort(s,s+m,cmp);double sum=0;for(int i=m-1;i>=0;--i)//從均值最大的房間開(kāi)始兌換 {if(n>=s[i].f)//如果剩余的貓糧多余房間所需貓糧 {n-=s[i].f;sum+=s[i].j;}else if(n<s[i].f&&n>0)//如果剩余貓糧少于房間所需貓糧 {sum+=s[i].avg*n;break;}}printf("%.3lf\n",sum); } }

    [小船過(guò)河問(wèn)題]有n個(gè)人想過(guò)河,每個(gè)人過(guò)河都有自己的過(guò)河時(shí)間,但只有一只小船,最多只能裝2個(gè)人,每一次過(guò)河,過(guò)河時(shí)間為用時(shí)最多的那人過(guò)河時(shí)間,如果還有人沒(méi)有過(guò)河,那么過(guò)去一個(gè)用時(shí)最少的送回船。問(wèn)n人過(guò)河最少要多少時(shí)間。

    分析:POJ-1700是一道經(jīng)典的貪心算法例題,在這道題中可以先將所有人過(guò)河所需的時(shí)間按照升序排序,我們考慮把單獨(dú)過(guò)河所需要時(shí)間最多的兩個(gè)旅行者送到對(duì)岸去,有兩種貪心策略:

    (1)最快的和次快的過(guò)河,然后最快的將船劃回來(lái);最慢的和次慢的過(guò)河,然后次快的將船劃回來(lái),所需時(shí)間為:t[0]+2*t[1]+t[n-1]

    (2)最快的和最慢的過(guò)河,然后最快的將船劃回來(lái);最快的和次慢的過(guò)河,然后最快的將船劃回來(lái),所需時(shí)間為:2*t[0]+t[n-2]+t[n-1]

    代碼:

    #include<bits/stdc++.h> using namespace std;
    int main() {int a[1000],t,n,sum;cin>>t;while(t--){cin>>n;sum=0;for(int i=0;i<n;i++)cin>>a[i];while(n>3){
    sum
    =min(sum+a[1]+a[0]+a[n-1]+a[1],sum+a[n-1]+a[0]+a[n-2]+a[0]);//選則兩種貪心策略中數(shù)值較小的一個(gè)n-=2;//一次可以送走兩個(gè)人 }if(n==3)sum+=a[0]+a[1]+a[2];//當(dāng)剩余人數(shù)等于3時(shí),0和1過(guò)河,0反回,然后0和2過(guò)河else if(n==2)sum+=a[1];else sum+=a[0];printf("%d\n",sum);} }

    五 結(jié)論?

    貪心算法既然被列為五大常用算法之一,肯定在算法中占據(jù)了不小的地位,雖然貪心思想比較簡(jiǎn)單,但熟練應(yīng)用貪心思想絕對(duì)是我們必備的技能,在一些最優(yōu)化問(wèn)題中常常需要運(yùn)用貪心思想,像數(shù)據(jù)結(jié)構(gòu)中的Huffman編碼,Dijkstra算法,Kruskal算法和Prim算法都能看見(jiàn)貪心思想的身影,所以千萬(wàn)一定不能小覷貪心算法!

    ?

    轉(zhuǎn)載于:https://www.cnblogs.com/wjw2018/p/9340526.html

    總結(jié)

    以上是生活随笔為你收集整理的五大常用算法之贪心算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。