題目:
很久很久之前,有一個人擁有5座金礦,每座金礦的黃金儲量不同,需要參與挖掘的人數也不同,例如有的黃金儲量是500KG黃金,需要5個工人挖掘,(200kg/3人,300kg/4人,350kg/3人,400kg/5人,500kg/5人)
如果參與挖礦的總數爲10,每座金礦要麼全挖,要麼不挖,用程序求出,應該挖取那幾座金礦。
到了現在自己好好思考一下自己的思路。。。
解題思路:(典型的動態規劃,和揹包問題類似)
先假設最後一個金礦註定不會被挖掘
然後其餘的金礦依次類推,當工人或者金礦爲0 的時候就是問題的邊界。具體如下圖:
將金礦數量設爲n,工人數量設爲w,金礦的含金量設爲g[],金礦所需開採人數爲數組p[],設F(n,w)爲n個金礦,w個工人的最優收益函數
轉換方程如下F(n,w) = 0(n=0或w=0)問題邊界爲金礦數爲0或者工人爲0,
當所剩工人不夠挖掘的時候,只有一種最優子結構
f(n,w) = F(n-1,w)(n>=1,w<p[n-1])
在常規情況下,具有兩種最優子結構(挖當前金庫或不挖當前金庫)
F(n,w) = max(F(n-1,w),F(n-1,w-p[n-1])+g[n-1])(n>=1,w>=p[n-1]),
代碼如下:
public class getBestGoldMining {
public static int getBestGoldMining(int w,int n,int[] p,int[] g){
if(w == 0 || n == 0){
return 0;
}
if(w<p[n-1]){
return getBestGoldMining(w,n-1,p,g);
}
return Math.max(getBestGoldMining(w,n-1,p,g),getBestGoldMining(w-p[n-1],n-1,p,g) +g[n-1]);
}
public static void main(String[] args){
int w = 10;
int[] p = {5,5,3,4,3};
int[] g = {400,500,200,300,350};
System.out.println("最優收益"+getBestGoldMining(w,g.length,p,g));
}
}
以上算法雖然可以求解出最優解,但是時間複雜度太高爲O(2^n)。用debug模式就可以看出許多方法調用是重複的,因此,我們應該避免這些重複調用。
這就要了解動態規劃中的自底而上求解。
最後程序只需要保存一行數據,從右向左統計,把舊的數據一個一個的替換掉。
//通過二維數組列表計算。
代碼如下:時間複雜度爲o(n)
public static int getBestGoldMiningV3(int w,int[] p,int[] g){
int[] results = new int[w+1];
for(int i=1;i<=g.length;i++){
for(int j = w;j>=1;j--){
if(j >= p[i-1]){
results[j] = Math.max(results[j],results[j-p[i-1]]+g[i-1]);
}
}
}
return results[w];
}