五大經常使用算法之三貪心算法

貪心算法

貪心算法簡介:

  貪心算法是指:在每一步求解的步驟中,它要求「貪婪」的選擇最佳操做,並但願經過一系列的最優選擇,可以產生一個問題的(全局的)最優解。java

  貪心算法每一步必須知足一下條件:算法

  一、可行的:即它必須知足問題的約束。數組

  二、局部最優:他是當前步驟中全部可行選擇中最佳的局部選擇。ide

  三、不可取消:即選擇一旦作出,在算法的後面步驟就不可改變了。this

貪心算法案例:

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

  用貪心法的話思想很簡單:活動越早結束,剩餘的時間是否是越多?那我就早最先結束的那個活動,找到後在剩下的活動中再找最先結束的不就得了?code

雖然貪心算法的思想簡單,可是貪心法不保證能獲得問題的最優解,若是得不到最優解,那就不是咱們想要的東西了,因此咱們如今要證實的是在這個問題中,用貪心法能獲得最優解。blog

java代碼實現:排序

  1 public class ActiveTime {
  2     public static void main(String[] args) {
  3         //建立活動並添加到集合中
  4         Active act1 = new Active(1, 4);
  5         Active act2 = new Active(3, 5);
  6         Active act3 = new Active(0, 6);
  7         Active act4 = new Active(5, 7);
  8         Active act5 = new Active(3, 8);
  9         Active act6 = new Active(5, 9);
 10         Active act7 = new Active(6, 10);
 11         Active act8 = new Active(8, 11);
 12         Active act9 = new Active(8, 12);
 13         Active act10 = new Active(2, 13);
 14         Active act11 = new Active(12, 14);
 15         List<Active> actives = new ArrayList<Active>();
 16         actives.add(act1);
 17         actives.add(act2);
 18         actives.add(act3);
 19         actives.add(act4);
 20         actives.add(act5);
 21         actives.add(act6);
 22         actives.add(act7);
 23         actives.add(act8);
 24         actives.add(act9);
 25         actives.add(act10);
 26         actives.add(act11);
 27         
 28         List<Active> bestActives  = getBestActives(actives, 0, 16);
 29         for (int i = 0; i < bestActives.size(); i++) {
 30             System.out.println(bestActives.get(i));
 31         }
 32     }
 33 
 34 
 35     /**
 36      * 
 37      * @param actives
 38      *            活動集合
 39      * @param startTime
 40      *            教室的開始使用時間
 41      * @param endTime
 42      *            教室的結束使用時間
 43      * @return
 44      */
 45     public static List<Active> getBestActives(List<Active> actives, int startTime, int endTime) {
 46         //最佳活動選擇集合
 47         List<Active> bestActives = new ArrayList<Active>();
 48         //將活動按照最先結束時間排序
 49         actives.sort(null);
 50         //nowTime 用來記錄上次活動結束時間
 51         int nowTime = startTime;
 52         /**
 53          * 由於咱們已經按照最先結束時間排序,那麼只要活動在時間範圍內
 54          * actives.get(1)就應當是第一個活動的結束時間.
 55          * 則咱們記錄第一次活動結束的時間,在結合剩下的活動中,
 56          * 選取開始時間大於nowTime且結束時間又在範圍內的活動,則爲第二次活動時間,
 57          * 知道選出全部活動
 58          */
 59         for (int i = 0; i < actives.size(); i++) {
 60             Active act = actives.get(i);
 61             if(act.getStartTime()>=nowTime&&act.getEndTime()<=endTime){
 62                 bestActives.add(act);
 63                 nowTime = act.getEndTime();
 64             }
 65         }
 66         return bestActives;
 67     }
 68 }
 69 
 70 /**
 71  * 活動類
 72  * @CreatTime 下午9:45:37
 73  *
 74  */
 75 class Active implements Comparable<Active>{
 76     private int startTime;//活動開始時間
 77     private int endTime;//活動結束時間
 78 
 79     public Active(int startTime, int endTime) {
 80         super();
 81         this.startTime = startTime;
 82         this.endTime = endTime;
 83     }
 84 
 85     public int getStartTime() {
 86         return startTime;
 87     }
 88 
 89     public void setStartTime(int startTime) {
 90         this.startTime = startTime;
 91     }
 92 
 93     public int getEndTime() {
 94         return endTime;
 95     }
 96 
 97     public void setEndTime(int endTime) {
 98         this.endTime = endTime;
 99     }
100     
101     @Override
102     public String toString() {
103         return "Active [startTime=" + startTime + ", endTime=" + endTime + "]";
104     }
105     
106     //活動排序時按照結束時間升序
107     @Override
108     public int compareTo(Active o) {
109         if(this.endTime>o.getEndTime()){
110             return 1;
111         }else if(this.endTime == o.endTime){
112             return 0;
113         }else{
114             return -1;
115         }
116     }
117 
118 
119 }

運行結果:get

Active [startTime=1, endTime=4]
Active [startTime=5, endTime=7]
Active [startTime=8, endTime=11]
Active [startTime=12, endTime=14]

能夠看出,求得的結果正好是最優解。


 

2.錢幣找零問題
這個問題在咱們的平常生活中就更加廣泛了。假設1元、2元、5元、10元、20元、50元、100元的紙幣分別有c0, c1, c2, c3, c4, c5, c6張。如今要用這些錢來支付K元,至少要用多少張紙幣?用貪心算法的思想,很顯然,每一步儘量用面值大的紙幣便可。在平常生活中咱們天然而然也是這麼作的。在程序中已經事先將Value按照從小到大的順序排好。

java代碼實現:

 1 package GreedyAlgorithm;
 2 
 3 public class CoinChange {
 4     public static void main(String[] args) {
 5         //人民幣面值集合
 6         int[] values = { 1, 2, 5, 10, 20, 50, 100 };
 7         //各類面值對應數量集合
 8         int[] counts = { 3, 1, 2, 1, 1, 3, 5 };
 9         //求442元人民幣需各類面值多少張
10         int[] num = change(442, values, counts);
11         print(num, values);
12     }
13 
14     public static int[] change(int money, int[] values, int[] counts) {
15         //用來記錄須要的各類面值張數
16         int[] result = new int[values.length];
17 
18         for (int i = values.length - 1; i >= 0; i--) {
19             int num = 0;
20             //須要最大面值人民幣張數
21             int c = min(money / values[i], counts[i]);
22             //剩下錢數
23             money = money - c * values[i];
24             //將須要最大面值人民幣張數存入數組
25             num += c;
26             result[i] = num;
27         }
28         return result;
29     }
30 
31     /**
32      * 返回最小值
33      */
34     private static int min(int i, int j) {
35         return i > j ? j : i;
36     }
37     
38     private static void print(int[] num, int[] values) {
39         for (int i = 0; i < values.length; i++) {
40             if (num[i] != 0) {
41                 System.out.println("須要面額爲" + values[i] + "的人民幣" + num[i] + "張");
42             }
43         }
44     }
45 }

運行結果:

須要面額爲2的人民幣1張
須要面額爲5的人民幣2張
須要面額爲10的人民幣1張
須要面額爲20的人民幣1張
須要面額爲100的人民幣4張

能夠看出,求出的結果也恰好等於442元。正好爲最優解。可是,當面額及數量爲下種特殊狀況時,貪心算法就沒法給出最優解。

//人民幣面值集合
 6         int[] values = { 3, 5, 10, 20, 50, 100 };
 7         //各類面值對應數量集合
 8         int[] counts = { 3, 2, 1, 1, 3, 5 };
須要求得money = 416元

運行結果以下:

須要面額爲5的人民幣1張
須要面額爲10的人民幣1張
須要面額爲100的人民幣4張

 因而咱們能夠看出,有些狀況,貪心算法確實能夠給出最優解,然而,還有一些問題並非這種狀況。對於這種狀況,咱們關心的是近似解,或者只能知足於近似解,貪心算法也是有價值的。

相關文章
相關標籤/搜索