[C++] 貪心算法之活動安排、背包問題
一、貪心算法的基本思想
在求解過程中,依據某種貪心標準,從問題的初始狀態出發,直接去求每一步的最優解,通過若幹次的貪心選擇,最終得出整個問題的最優解。
從貪心算法的定義可以看出,貪心算法不是從整體上考慮問題,它所做出的選擇只是在某種意義上的局部最優解,而由問題自身的特性決定了該題運用貪心算法可以得到最優解。如果一個問題可以同時用幾種方法解決,貪心算法應該是最好的選擇之一。
二、貪心算法的基本要素
(1)最優子結構性質
(2)貪心選擇性質(局部最優選擇)
三、貪心算法實例
1、活動安排
設有n個活動的集合 E = {1,2,…,n},其中每個活動都要求使用同一資源,如演講會場等,而在同一時間內只有一個活動能使用這一資源。
每個活動 i 都有一個要求使用該資源的起始時間 si 和一個結束時間 fi,且 si < fi。如果選擇了活動i,則它在半開時間區間 [si ,fi ) 內占用資源。若區間 [si , fi )與區間 [sj, fj ) 不相交,則稱活動i與活動j是相容的。當 si ≥ fj 或 sj ≥ fi 時,活動 i 與活動 j 相容。
活動安排問題就是在所給的活動集合中選出最大的相容活動子集合。
例如:
1 #include <iostream> 2 using namespace std; 3 4 #define NUM 50 5 6 voidGreedySelector(int n, int s[], int f[], bool b[]) 7 { 8 b[1]=true; //默認將第一個活動先安排 9 int j=1; //記錄最近一次加入b中的活動 10 11 //依次檢查活動i是否與當前已選擇的活動相容 12 for(int i=2;i<=n;i++) 13 { 14 if (s[i]>=f[j]) 15 { 16 b[i]=true; 17 j=i;18 } 19 else 20 b[i]=false; 21 } 22 } 23 24 int main() 25 { 26 int s[] = {0,1,3,0,5,3,5,6,8,8,2,12}; //存儲活動開始時間 27 int f[] = {0,4,5,6,7,8,9,10,11,12,13,14}; //存儲活動結束時間 28 bool b[NUM]; //存儲被安排的活動編號 29 int n = (sizeof(s) / sizeof(s[0])) - 1; 30 31 GreedySelector(n, s, f, b); 32 33 for(int i = 1; i <= n; i++) //輸出被安排的活動編號和它的開始時間和結束時間 34 { 35 if(b[i]) cout << "活動 " << i << " :" << "(" << s[i] << "," << f[i] << ")" <<endl; 36 } 37 return 0; 38 }
2、背包問題
給定一個載重量為 M 的背包,考慮 n 個物品,其中第 i 個物品的重量 wi(1 ≤ i ≤ n),價值 vi(1 ≤ i ≤ n),要求把物品裝滿背包,且使背包內的物品價值最大。
有兩類背包問題(根據物品是否可以分割),如果物品不可以分割,稱為 0—1 背包問題(動態規劃);如果物品可以分割,則稱為背包問題(貪心算法)。
例如:
有3種方法來選取物品:
(1)當作 0—1 背包問題,用動態規劃算法,獲得最優值 220;
(2)當作 0—1 背包問題,用貪心算法,按性價比從高到底順序選取物品,獲得最優值 160。由於物品不可分割,剩下的空間白白浪費。
(3)當作背包問題,用貪心算法,按性價比從高到底的順序選取物品,獲得最優值 240。由於物品可以分割,剩下的空間裝入物品 3 的一部分,而獲得了更好的性能。
圖2.1 背包問題
1 #include <iostream> 2 using namespace std; 3 4 #define NUM 50 5 6 //這裏假設 w[], v[] 已按要求排好序 7 void Knapsack(int n,float M,float v[],float w[],float x[]) 8 { 9 int i; 10 for(i = 1; i <= n; i++) x[i] = 0; //初始化數組 11 float c = M; 12 for(i = 1;i <= n; i++) //全部被裝下的物品,且將 x[i] = 1 13 { 14 if(w[i]>c) break; 15 x[i] = 1; 16 c -= w[i]; 17 } 18 19 if(i <= n) x[i] = c / w[i]; //將物品i 的部分裝下 20 } 21 22 int main() 23 { 24 float M = 50; //背包所能容納的重量 25 float w[] = {0,10,20,30}; //這裏給定的物品按價值降序排序 26 float v[] = {0,60,100,120}; 27 float x[NUM]; //存儲每個物品裝入背包的比例 28 29 int n = (sizeof(w) / sizeof(w[0])) - 1; 30 31 Knapsack(n, M, v, w, x); 32 33 for(int i = 1; i <= n; i++) 34 cout << "物品 " << i << " 裝入的比例: " << x[i] << endl; 35 return 0; 36 }
[C++] 貪心算法之活動安排、背包問題