算法:貪婪算法基礎
理解貪心算法
說明
貪心算法是使所做的選擇看起來都是當前最佳的,期望通過所做的局部最優選擇來產生一個全局最優解。
設計貪心算法的步驟
1.將優化問題轉換成這樣一個問題,即先做出選擇,再解決剩下的一個子問題。
2.證明原問題總是有一個最優解是貪心選擇的得到的,從而說明貪心選擇的安全。
3.說明在做出貪心選擇后,剩下的子問題具有這樣一個性質。即如果將子問題的最優解和我們所做的貪心選擇聯合起來,可以得到一個更加負責的動態規划解。
剪繩子
問題描述
給你一個長度為n的繩子,請把繩子剪成m段(m,n都是整數,且都大於1)每段繩子的長度即為K[0],K[1],K[2]...K[m]。請問K[0]*k[1]..*k[m]可能的最大乘積是多少?
解決思路
如果我們按照如下的策略剪繩子,則得到的各段繩子的長度的乘積將最大;當n>=5,我們盡可能地剪長度為3的繩子;當剩下的繩子長度為4時,把繩子剪為長度為2的繩子.
貪心算法的核心是通過局部最優解來得到全局最優解,對於分割問題來說,要使乘積最大,該問題的貪心思想是盡可能去剪為長度為3的繩子!
Java代碼
迭代法
public static int greedy_cut_rope_1(int n) { if(n<2) return 0; if(n==2) return 1; if(n==3) return 2; //盡可能多地去減長度為3的繩子段 int timesOf3 = n/3; //當繩子最后剩下的長度為4的時候,不能再去剪去長度為3的繩子段 if(n-timesOf3*3==1) timesOf3-=1; int timesOf2 =(n-timesOf3*3)/2; return (int) (Math.pow(3,timesOf3)*Math.pow(2,timesOf2)); }
遞歸法
public static int greedy_cut_rope(int n) { if(n==2) return 2; if(n==3) return 3; if(n<2) return 1; //int timesOf3 = n/3; if(n==4) return 4; return 3*greedy_cut_rope(n-3); }
背包問題
問題描述
給定N個物品和一個容量為C的背包,物品i的重量為Wi,其價值為Vi,背包問題是如何選擇裝入背包的物品,使得裝入背包中物品的總價值最大。注意在背包問題中,可以將某種物品的一部分裝入背包中,但是不可以重復裝入。
解決思路
三種貪心思想:
- 選擇價值最大的物品
- 選擇重量最輕的物品
- 選擇單位重量價值最大的物品
毫無疑問,我們當然選擇第三種咯。先把性價比最高的全部裝入,最后不足全部裝入的部分裝入。
Java實現代碼
public static int greedy_knapSack(int[] w,int[] v,int n,int c) { // 假設物品已按單位重量降序排列 double[] x = new double[10]; int maxValue =0; int i; for(i=0;w[i]<c;i++) { x[i]=1; //將物品 i 裝入背包 maxValue+=v[i]; c=c-w[i]; // 背包剩余數量 } x[i]=(double)c/w[i]; //物品i裝入一部分 maxValue+=x[i]*v[i]; return maxValue; //返回背包獲得的價值 }
活動選擇問題
問題描述
假設有一個需要使某一資源的n個活動組成的集合S={a1,a2,a3...an}。該資源一次只能被一個活動占用,每個活動ai有一個開始時間Si和結束時間Fi,且0<=Si<Fi<∞。一旦被選擇后,活動ai就占據半開時間區間[Si,Fi)。如果區間[Si,Fi)與 [Sj,Fj)互不重疊,稱活動ai與aj是兼容的。活動選擇問題就是要選擇出一個由互相兼容的問題組成的最大集合。
討論下面的活動集合S,其中各活動已按結束時間的單調遞增順序進行了排序:
解決思路
對於任意非空子問題Sij,設am是Sij中具有最早結束時間的活動:
fm=min{fk:ak∈Sij}
那么:
1.活動am在Sij的某最大兼容活動子集中被使用。
2.子問題Sim為空,所以選擇am使子問題Smj為唯一可能非空的子問題
在解決子問題時,選擇am是一個可被合法調度、具有最早結束時間的活動。從直覺上來看,這種活動選擇方法是一種貪婪技術,他給后面剩下的待調度任務留下了盡可能多的機會。也就是說,此處的貪心選擇使得剩下的、未調度的時間最大化。
Java實現代碼
迭代貪心算法
public static void greedy_activity_selector(int[] s,int[] f,boolean[] b) { int n = s.length-1; b[1]=true; int j=1; for(int i =2;i<=n;i++) { if(s[i]>f[j]) { b[i]=true; j=i; }else b[i]=false; } for(int i=1;i<b.length;i++) System.out.println(b[i]); }