從零開始學貪心演算法

2021-07-31 10:39:10 字數 3316 閱讀 5114

貪心演算法的定義:

貪心演算法是指在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,只做出在某種意義上的區域性最優解。貪心演算法不是對所有問題都能得到整體最優解,關鍵是貪心策略的選擇,選擇的貪心策略必須具備無後效性,即某個狀態以前的過程不會影響以後的狀態,只與當前狀態有關。

解題的一般步驟是:

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

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

3.對每一子問題求解,得到子問題的區域性最優解;

4.把子問題的區域性最優解合成原來問題的乙個解。

如果大家比較了解動態規劃,就會發現它們之間的相似之處。最優解問題大部分都可以拆分成乙個個的子問題,把解空間的遍歷視作對子問題樹的遍歷,則以某種形式對樹整個的遍歷一遍就可以求出最優解,大部分情況下這是不可行的。貪心演算法和動態規劃本質上是對子問題樹的一種修剪,兩種演算法要求問題都具有的乙個性質就是子問題最優性(組成最優解的每乙個子問題的解,對於這個子問題本身肯定也是最優的)。動態規劃方法代表了這一類問題的一般解法,我們自底向上構造子問題的解,對每乙個子樹的根,求出下面每乙個葉子的值,並且以其中的最優值作為自身的值,其它的值捨棄。而貪心演算法是動態規劃方法的乙個特例,可以證明每乙個子樹的根的值不取決於下面葉子的值,而只取決於當前問題的狀況。換句話說,不需要知道乙個節點所有子樹的情況,就可以求出這個節點的值。由於貪心演算法的這個特性,它對解空間樹的遍歷不需要自底向上,而只需要自根開始,選擇最優的路,一直走到底就可以了。

話不多說,我們來看幾個具體的例子慢慢理解它:

1.活動選擇問題

這是《演算法導論》上的例子,也是乙個非常經典的問題。有n個需要在同一天使用同乙個教室的活動a1,a2,…,an,教室同一時刻只能由乙個活動使用。每個活動ai都有乙個開始時間si和結束時間fi 。一旦被選擇後,活動ai就佔據半開時間區間[si,fi)。如果[si,fi]和[sj,fj]互不重疊,ai和aj兩個活動就可以被安排在這一天。該問題就是要安排這些活動使得盡量多的活動能不衝突的舉行。例如下圖所示的活動集合s,其中各項活動按照結束時間單調遞增排序。

考慮使用貪心演算法的解法。

可以用數學歸納法證明,我們的貪心策略應該是每次選取結束時間最早的活動。直觀上也很好理解,按這種方法選擇相容活動為未安排活動留下盡可能多的時間。這也是把各項活動按照結束時間單調遞增排序的原因。

解析:預設從早上開始排,結束的早剩下的就能多放點,參考1-5,6-8和4-7;

#include

#include

#include

using

namespace

std;

int n;

struct act

act[100010];

bool cmp(act a,act b)

} return num;

}int main()

act[0].start=-1;

act[0].end=-1;

sort(act+1,act+n+1,cmp);

int res=greedy_activity_selector();

cout

<2.小船過河問題

poj1700是一道經典的貪心演算法例題。題目大意是只有一艘船,能乘2人,船的執行速度為2人中較慢一人的速度,過去後還需乙個人把船划回來,問把n個人運到對岸,最少需要多久。先將所有人過河所需的時間按照公升序排序,我們考慮把單獨過河所需要時間最多的兩個旅行者送到對岸去,有兩種方式:

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

2.最快的和最慢的過河,然後最快的將船划回來,最快的和次慢的過河,然後最快的將船划回來,所需時間為:2*t[0]+t[n-2]+t[n-1]。

算一下就知道,除此之外的其它情況用的時間一定更多。每次都運送耗時最長的兩人而不影響其它人,問題具有貪心子結構的性質。

ac**:

#include

#include

using namespace std;

int main()

if(n==3) sum+=a[0]+a[1]+a[2];

else

if(n==2) sum+=a[1];

else

sum+=a[0];

printf("%d\n",sum);}}

3.銷售比賽

在學校oj上做的一道比較好的題,這裡碼一下。假設有偶數天,要求每天必須買一件物品或者賣一件物品,只能選擇一種操作並且不能不選,開始手上沒有這種物品。現在給你每天的物品**表,要求計算最大收益。首先要明白,第一天必須買,最後一天必須賣,並且最後手上沒有物品。那麼除了第一天和最後一天之外我們每次取兩天,小的買大的賣,並且把賣的**放進乙個最小堆。如果買的**比堆頂還大,就交換。這樣我們保證了賣的**總是大於買的**,一定能取得最大收益。

#include

#include

#include

#include

#include

#include

#include

using

namespace

std;

long

long

int price[100010],t,n,res;

int main()

res-=price[1];

res+=price[n];

for(int i=2;i<=n-1;i=i+2)

else

}else

}

cout

<4.dijkstra演算法

dijkstra演算法是由e.w.dijkstra於2023年提出,是目前公認的最好的求解最短路徑的方法,使用的條件是圖中不能存在負邊。演算法解決的是單個源點到其他頂點的最短路徑問題,其主要特點是每次迭代時選擇的下乙個頂點是標記點之外距離源點最近的頂點,簡單的說就是bfs+貪心演算法的思想。

#include

#include

#define inf 1000

#define max_v 100

using

namespace

std;

int main()

}for(m=0;mcin>>i>>j>>cost[i][j];

cost[j][i]=cost[i][j];

}cin>>n;

d[n]=0;

//源點

while(true)

}

零開始學python 從零開始學Python

第1章 python入門 1 1 1 什麼是python 1 1 2 python語言有什麼特點 2 1 3 python可以幹什麼 4 練一練 5 第2章 準備開發環境 6 2 1 在windows上安裝python開發環境 6 2 2 選擇和安裝開發工具 11 練一練 17 第3章 基本概念 1...

從零開始學演算法 階乘求和

描述 求sn 1 2 3 4 5 n 之值,其中n是乙個數字 n不超過20 使用語言 c 我的分析 直接計算,沒有什麼技術 我的 結果錯誤的錯誤的 include using namespace std intmain cout return0 然而 我都已經防它一手了,再出這個結果就很有意思了。這...

從零開始學android

相對布局管理器指的是參考某一其他控制項進行擺放,可以通過控制,將元件擺放在乙個指定參考元件的上 下 左 右等位置,這些可以直接通過各個元件提供的屬性完成。下面介紹一下各個方法的基本使用 no.屬性名稱 對應的規則常量 描述1 android layout below relativelayout.b...