由於我司舉辦一個演算法程式設計大賽,隨機抽籤下面圖片的演算法題目,想了一段時間記起之前在書(演算法圖解)上有一個演算法比較符合,那就是動態規劃中的「背包問題」。
背包問題(Knapsack problem)是一種組合優化的NP完全問題。問題可以描述為:給定一組物品,每種物品都有自己的重量和價格,在限定的總重量內,我們如何選擇,才能使得物品的總價格最高。
如何選擇最合適的物品放置於給定背包中,與我們的題目相符合,所以這次我們使用的是「0-1背包問題」,用我們這次的題目進行代入,「總人數」 等價於 「背包」,「物品」 等價於 「工單型別」,物品的重量就是所需人數。
補充:
背包問題解法延伸問題有三個:無界背包問題、0-1背包問題、二次背包問題 (不做詳細延伸,只需我們使用的)
演算法題目如下
動態規劃所處理的問題是一個多階段決策問題,一般由初始狀態開始,通過對中間階段決策的選擇,達到結束狀態。這些決策形成了一個決策序列,同時確定了完成整個過程的一條活動路線(通常是求最優的活動路線)。動態規劃的設計都有著一定的模式,一般要經歷以下幾個步驟。
初始狀態→│決策1│→│決策2│→…→│決策n│→結束狀態
動態規劃解題公式:
f(n,m)=max{f(n-1,m),f(n-1,m-w[n])+P(n,m)}
● 橫向m(總人數),縱向n(4輛車做的工單型別)
● f(n-1,m) ==> (決策1)上一個工單型別對應的技師人數,做的工單利潤
● P(n,m) ==> (決策2)當前工單型別對應的技師人數,做的工單利潤
● f(n-1,m-w[n]) ==> 用減去當前工單所需人數,在上一個決策中對應人數的利潤
所以最優解的答案是:決策n中五個技師中對應的值,1799元
PostMan提交引數:
people:5 carDetail[0][technician]:2 carDetail[0][amount]:49 carDetail[0][type]:全車安檢 carDetail[1][technician]:2 carDetail[1][amount]:499 carDetail[1][type]:深度診斷 carDetail[2][technician]:3 carDetail[2][amount]:1300 carDetail[2][type]:二手車檢測 carDetail[3][technician]:1 carDetail[3][amount]:10 carDetail[3][type]:空調專項檢測
解答方式一:動態規劃
<?php // 動態規劃 error_reporting(E_ALL ^ E_NOTICE); $t1 = microtime(true); class bestMatch { public function getMethod($postData) { $peopleArr = $gainArr = $nameArr = [0]; foreach ($postData['carDetail'] as $val) { // 初始化各個套餐:所需人數、利潤和套餐名稱陣列 $peopleArr[] = $val['technician']; $gainArr[] = $val['amount']; $nameArr[] = $val['type']; } // 獲取人數總數(背包) $totalPeople = $postData['people']; // 做檢測單數 $items = count($peopleArr); // 利潤列表 - 初始狀態 $cacheMap[] = array_fill(1, $items, 0); // 套餐列表 - 初始狀態 $cacheMapName[] = array_fill(1, $items, ''); //中間的各種決策(依次放入物品a,b,c,d,e) // 第一個迴圈是總人數 for($i = 1; $i <= $totalPeople; $i++) { // 第二個迴圈是套餐 for($j = 1; $j < $items; $j++) { $requiredPeople = $peopleArr[$j]; $gain = $gainArr[$j]; $name = $nameArr[$j]; // 上一行坐標數 $preLine = $j-1; $prevGain = $cacheMap[$preLine][$i]; $prevName = $cacheMapName[$preLine][$i]; if($requiredPeople > $i) { $cacheMap[$j][$i] = $prevGain; $cacheMapName[$j][$i] = $prevName; } else { // 剩餘價值 if ($i-$requiredPeople >= 0) { $surplusPeople = $i-$requiredPeople; $surplusGain = $cacheMap[$preLine][$surplusPeople]; $surplusName = $cacheMapName[$preLine][$surplusPeople]; }else { $surplusGain = 0; $surplusName = ''; } $nowTotalGain = $gain + $surplusGain; $cacheMap[$j][$i] = max($prevGain, $nowTotalGain); if ($prevGain > $nowTotalGain) { $cacheMapName[$j][$i] = $prevName; }else{ $cacheMapName[$j][$i] = $name.'+'.$surplusName; } } } } $actual = count($postData['carDetail']); return [ 'maxMatch' => $cacheMap[$actual][$totalPeople], 'maxMatchName' => trim($cacheMapName[$actual][$totalPeople],'+') ]; } } $bestMatch = new bestMatch; if (empty($_POST) || isset($_POST['people']) && $_POST['people'] > 0) { die('提交引數有誤'); } $res = $bestMatch->getMethod($_POST); $t2 = microtime(true); echo '動態規劃: '.'<br/>'; echo '最佳金額: '.$res['maxMatch'].'<br/>'; echo '最佳套餐搭配: '.$res['maxMatchName'].'<br/>'; echo '耗時'.round($t2-$t1,7).'秒'.'<br/>' ; echo '消耗記憶體: ' . memory_get_usage().'位元組'.'<br/>' ;
解答方式二:遞回
<?php // 遞回查詢 error_reporting(E_ALL ^ E_NOTICE); $t1 = microtime(true); class optimal { public function getSortList($array,$index = 0,$up =0,&$result =[]) { for ($i=$index; $i < count($array); $i++) { if($index > 0 ){ $value['name'] = $up['name'].'+'.$array[$i]['type']; $value['amount'] = bcadd($up['amount'],$array[$i]['amount']); $value['technician'] = bcadd($up['technician'],$array[$i]['technician']); }else{ $value['name'] = $array[$i]['type']; $value['amount'] = bcadd($array[$i]['amount'],0); $value['technician'] = bcadd($array[$i]['technician'],0); } $result[] = $value; $this->getSortList($array,$i+1,$value,$result); } return $result ; } public function getMethod($postData) { $people = $postData['people']; $carDetail = $postData['carDetail']; $allResult = $this->getSortList($carDetail); $bestMatch = []; foreach ($allResult as $val) { if ($val['technician'] <= $people) { if ($bestMatch) { if ($val['amount'] > $bestMatch['amount']) { $bestMatch = $val; } }else{ $bestMatch = $val; } } } return $bestMatch; } } $optimal = new optimal(); if (empty($_POST) || isset($_POST['people']) && $_POST['people'] > 0) { die('提交引數有誤'); } $bestMatch = $optimal->getMethod($_POST); $t2 = microtime(true); echo '遞回查詢: '.'<br/>'; echo '最佳金額: '.$bestMatch['amount'].'<br/>'; echo '最佳套餐搭配: '.$bestMatch['name'].'<br/>'; echo '耗時'.round($t2-$t1,7).'秒'.'<br/>' ; echo '消耗記憶體: ' . memory_get_usage().'位元組'.'<br/>' ;
以上就是PHP實現動態規劃之背包問題的詳細內容,更多請關注TW511.COM其它相關文章!