事情原由
由於我司舉辦一個演算法程式設計大賽,隨機抽籤下面圖片的演算法題目,想了一段時間記起之前在書(演算法圖解)上有一個演算法比較符合,那就是動態規劃中的「背包問題」。
背包問題(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實作動態規劃之背包問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!