[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 void GreedySelector(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 }

 


免責聲明!

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



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