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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

五大常用算法之贪心算法

發布時間:2023/12/10 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 五大常用算法之贪心算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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

有人歸納了計算機的五大常用算法,它們是貪心算法,動態規劃算法,分治算法,回溯算法以及分支限界算法。雖然不知道為何要將這五個算法歸為最常用的算法,但是毫無疑問,這五個算法是有很多應用場景的,最優化問題大多可以利用這些算法解決 ,接下來介紹的就是貪心算法。

一 貪心算法的概念?

所謂貪心算法,是指在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。舉個例子就很清楚了:現在有一個能裝4斤蘋果的袋子,蘋果有兩種,一種3斤一個,一種2斤一個,怎么裝才能得到最多蘋果?當然如果是我們人考慮的話當然是拿兩個2斤的蘋果,就剛好裝滿了,但是如果按貪心算法拿的話,首先就要把最重的那個3斤的蘋果拿下(局部最優),但其實拿2個2斤的蘋果能剛好裝4斤,所以并沒有得到最多蘋果(整體最優)。

二 什么問題適合用貪心算法

對于一個詳細的問題,怎么知道是否可用貪心算法解此問題,以及是否能得到問題的最優解? 我們能夠依據貪心法的倆個重要的性質去證明:貪心選擇性質和最優子結構性質。

貪心選擇:什么叫貪心選擇?從字義上就是貪心也就是目光短線。貪圖眼前利益。在算法中就是僅僅依據當前已有的信息就做出選擇,并且以后都不會改變這次選擇。(這是和動態規劃法的主要差別)所以對于一個詳細問題。要確定它是否具有貪心選擇性質,必須證明每做一步貪心選擇是否終于導致問題的總體最優解

最優子結構:當一個問題的最優解包括其子問題的最優解時,稱此問題具有最優子結構性質。這個性質和動態規劃法的一樣,是可用動態規劃算法或貪心算法求解的關鍵特征。

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

三 貪心算法的求解過程?

一般求解過程:

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

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

    {

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

    ??? while(not solve(S)) //集合S沒有構成問題的一個解

    ??? {

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

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

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

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

    ??? }

    ??? return S;

    }

    四 經典案例分析?

    [活動選擇問題]這是《算法導論》上的一個案例,也是一個非常經典的問題:

    學校只有一個教室,下面表格i代表活動的編號,s代表活動開始時間,f代表活動結束時間,現在問題是怎么安排這些活動使得盡量多的活動能不沖突的舉行?

    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 活動時間表

    用動態規劃的話較麻煩:假設我已經知道第k個活動是活動序列之一,那么又把1到k和k到11看做兩個子問題繼續分下去。

    用貪心算法的話很簡單:活動越早結束,剩余的時間越多,那就找到最早結束的那個活動,找到后在剩下的活動中再找最早結束的,最后輸出找到的總數。

    貪心選擇示例:

    圖 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; } }

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

    [0-1背包問題]有一個背包,背包容量是M=150。有7個物品,有著各自的重量和價值。現要求盡可能讓裝入背包中的物品總價值最大,但不能超過總容量。

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

    重量 35 30 60 50 40 10 25

    價值 10 40 30 50 35 40 30

    分析:

    目標函數:∑pi最大

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

    3種貪心策略:

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

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

    (3)每次選取單位重量價值最大的物品裝入背包,是否能得到最優解?

    證明:

    一般來說,貪心算法的證明圍繞著:整個問題的最優解一定由在貪心策略中存在的子問題的最優解得來的。

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

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

    W=30

    物品:A ?B ?C

    重量:28 12 12

    價值:30 20 20

    根據策略,首先選取物品A,接下來就無法再選取了,可是,選取B、C則更好。

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

    (3)貪心策略:選取單位重量價值最大者。反例:

    W=30

    物品:A ?B ?C

    重量:28 20 10

    價值:28 20 10

    根據策略,三種物品單位重量價值一樣,程序無法依據現有策略作出判斷,如果選擇物品A,則答案錯誤。

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

    [老鼠交易問題]老鼠準備了M磅的貓糧,準備與守衛他最喜歡的食物JavaBean的倉庫的貓交易。該倉庫有N個房間。第i個房間包含J[i]磅的JavaBeans并且需要F[i]磅的貓糧。老鼠不需要為房間里的所有JavaBeans進行交易,相反,如果他支付F[i]*a%磅的貓糧,他會得到J[i]*a%磅的JavaBeans。現在要求出老鼠能得到的最多的JavaBean的數量。

    分析:這道題其實就是一個0-1背包問題,只不過物品可以被分割的放進背包中,所以我們的貪心策略當然是選取單位數量價值最大者放進背包。

    代碼:

    #include<bits/stdc++.h> using namespace std;
    struct jb {double j,f,avg;//用于保存每個房間的信息 };
    bool cmp(jb x,jb y) {return x.avg<y.avg;//定義排序規則,即均值小的排在前面 }
    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)//從均值最大的房間開始兌換 {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); } }

    [小船過河問題]有n個人想過河,每個人過河都有自己的過河時間,但只有一只小船,最多只能裝2個人,每一次過河,過河時間為用時最多的那人過河時間,如果還有人沒有過河,那么過去一個用時最少的送回船。問n人過河最少要多少時間。

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

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

    (2)最快的和最慢的過河,然后最快的將船劃回來;最快的和次慢的過河,然后最快的將船劃回來,所需時間為: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]);//選則兩種貪心策略中數值較小的一個n-=2;//一次可以送走兩個人 }if(n==3)sum+=a[0]+a[1]+a[2];//當剩余人數等于3時,0和1過河,0反回,然后0和2過河else if(n==2)sum+=a[1];else sum+=a[0];printf("%d\n",sum);} }

    五 結論?

    貪心算法既然被列為五大常用算法之一,肯定在算法中占據了不小的地位,雖然貪心思想比較簡單,但熟練應用貪心思想絕對是我們必備的技能,在一些最優化問題中常常需要運用貪心思想,像數據結構中的Huffman編碼,Dijkstra算法,Kruskal算法和Prim算法都能看見貪心思想的身影,所以千萬一定不能小覷貪心算法!

    ?

    轉載于:https://www.cnblogs.com/wjw2018/p/9340526.html

    總結

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

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。