題目
有一個國家發現了5座金礦,每座金礦的黃金儲量不同,需要參與挖掘的工人數也不同。參與挖礦工人的總數是10人。
每座金礦要麼全挖,要麼不挖,不能派出一半人挖取一半金礦。要求用程式求解出,要想得到儘可能多的黃金,應該選擇挖取哪幾座金礦?
工人:10名
金礦:400金/5人、500金/5人、200金/3人、300金/4人、350金/3人、
動態規劃問題建模階段
設金礦數量為 N, 工人數量為 W, 金礦黃金量為 G[], 金礦用工量為 P[]
工人數 | 1 2 3 4 5 6 7 8 9 10
——————————-+———————————————————————————————————————————————
第一座金礦 | 0 0 0 0 400 400 400 400 400 400
第二座金礦 | 0 0 0 0 500 500 500 500 500 900
第三座金礦 | 0 0 200 200 500 500 500 700 700 900
第四座金礦 | 0 0 200 300 500 500 500 700 800 900
第五座金礦 | 0 0 350 350 500 550 650 850 850 900
規律:
3金礦8工人 = max( 2金礦8工人, 2金礦5工人+第三座金礦) = max(500, 500+200)
5金礦10工人 = max( 4金礦10工人, 4金礦7工人+第五座金礦) = max(900, 500+350)
……
由此可見,只需要儲存前一行的結果,就可以推匯出新的一行。
動態規劃三個重要的概念
最優子結構:
由於第五座金礦可挖可不挖,所以10人5座金礦的最優子結構有兩種(選最大值):
10人4座金礦的挖金總量 和 前四座金礦的挖金總量+第五座金礦的挖金總量
= Max( F(4,10), F( 4,10-P[4] ) + G[4] )
邊界:
只有一座金礦時,如果工人夠,那就是金礦的黃金量;如果工人不夠,挖金總量就是0
當 N = 1, W >= P[0]時, F(N,W) = G[0];
當 N = 1, W < P[0]時, F(N,W) = 0
轉態轉移公式:
F(N,W) = 0 ,( N <= 1, W < P[0] ) 小於等於一座金礦,工人人數不夠開挖
F(N,W) = G[0] ,( N == 1, W >= P[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] ) 取最後一座金礦挖與不挖的最大值
動態規劃求解問題階段
class Index extends TestCase
{
/**
* 動態規劃(Dynamic Programming)
* 時間複雜度:O(N * W)
* 空間複雜度:O(W)
*/
public function test()
{
$N = 5; // 金礦數量
$W = 10; // 工人數量
$P = array(5, 5, 3, 4, 3); // 每座金礦需要的用工數
$G = array(400, 500, 200, 300, 350); // 每座金礦的金礦重量
$pre = $res = array(); // 儲存前一行結果、最終結果
// 只有一座金礦時的情況
for ($i = 0; $i <= $W; $i++) {
if ($i < $P[0]) { // 如果當前迴圈工人數 i < 第一座金礦所需工人數 P[0],則為 0
$pre[$i] = 0;
} else { // 否則就是第一座金礦的重量
$pre[$i] = $G[0];
}
}
// 迴圈金礦,即迭代每一層
for ($i = 0; $i < $N; $i++) {
// 迴圈工人數
for ($j = 0; $j <= $W; $j++) {
if ($j < $P[$i]) { // 如果工人數不夠挖最後一座金礦
$res[$j] = $pre[$j];
} else { // 如果工人數夠挖最後一座
$res[$j] = max($pre[$j], $pre[$j - $P[$i]] + $G[$i]);
}
}
$pre = $res;
}
halt($res[$N]);
}
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結