因爲我司舉辦一個算法編程大賽,隨機抽籤下面圖片的算法題目,想了一段時間記起以前在書(算法圖解)上有一個算法比較符合,那就是動態規劃中的「揹包問題」。php
揹包問題(Knapsack problem)是一種組合優化的NP徹底問題。問題能夠描述爲:給定一組物品,每種物品都有本身的重量和價格,在限定的總重量內,咱們如何選擇,才能使得物品的總價格最高。算法
如何選擇最合適的物品放置於給定揹包中,與咱們的題目相符合,因此此次咱們使用的是「0-1揹包問題」,用咱們此次的題目進行代入,「總人數」 等價於 「揹包」,「物品」 等價於 「工單類型」,物品的重量就是所需人數。編程
補充:數組
揹包問題解法延伸問題有三個:無界揹包問題、0-1揹包問題、二次揹包問題 (不作詳細延伸,只需咱們使用的)bash
算法題目以下post
動態規劃所處理的問題是一個多階段決策問題,通常由初始狀態開始,經過對中間階段決策的選擇,達到結束狀態。這些決策造成了一個決策序列,同時肯定了完成整個過程的一條活動路線(一般是求最優的活動路線)。動態規劃的設計都有着必定的模式,通常要經歷如下幾個步驟。優化
初始狀態→│決策1│→│決策2│→…→│決策n│→結束狀態ui
動態規劃解題公式:this
f(n,m)=max{f(n-1,m),f(n-1,m-w[n])+P(n,m)}spa
橫向m(總人數),縱向n(4輛車作的工單類型)
f(n-1,m) ==> (決策1)上一個工單類型對應的技師人數,作的工單利潤
P(n,m) ==> (決策2)當前工單類型對應的技師人數,作的工單利潤
f(n-1,m-w[n]) ==> 用減去當前工單所需人數,在上一個決策中對應人數的利潤
因此最優解的答案是:決策n中五個技師中對應的值,1799元
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/>' ;
複製代碼
參考文章連接:
書籍:算法圖解