据说,我在Rosetta Code发现了一个绝对漂亮的实现,并清理了一些变量名,以帮助我从一个非常基本的角度更好的理解这一点.
不幸的是,我有一个难以置信的困难时间,弄清楚我如何应用这个逻辑来包括项目组.我的目的是建立幻想团队,提供我自己的价值&每个玩家的体重(积分/工资),但没有组(我的情况下的位置)我无法这样做.
有人可以指出我正确的方向吗?我正在审查来自其他语言的代码示例以及对整个问题的其他说明,但是我想通过任何可能的方式来实现组.
- <?PHP
- function knapSolveFast2($itemWeight,$itemValue,$i,$availWeight,&$memoItems,&$pickedItems)
- {
- global $numcalls;
- $numcalls++;
- // Return memo if we have one
- if (isset($memoItems[$i][$availWeight]))
- {
- return array( $memoItems[$i][$availWeight],$memoItems['picked'][$i][$availWeight] );
- }
- else
- {
- // At end of decision branch
- if ($i == 0)
- {
- if ($itemWeight[$i] <= $availWeight)
- { // Will this item fit?
- $memoItems[$i][$availWeight] = $itemValue[$i]; // Memo this item
- $memoItems['picked'][$i][$availWeight] = array($i); // and the picked item
- return array($itemValue[$i],array($i)); // Return the value of this item and add it to the picked list
- }
- else
- {
- // Won't fit
- $memoItems[$i][$availWeight] = 0; // Memo zero
- $memoItems['picked'][$i][$availWeight] = array(); // and a blank array entry...
- return array(0,array()); // Return nothing
- }
- }
- // Not at end of decision branch..
- // Get the result of the next branch (without this one)
- list ($without_i,$without_PI) = knapSolveFast2($itemWeight,$i-1,$memoItems,$pickedItems);
- if ($itemWeight[$i] > $availWeight)
- { // Does it return too many?
- $memoItems[$i][$availWeight] = $without_i; // Memo without including this one
- $memoItems['picked'][$i][$availWeight] = array(); // and a blank array entry...
- return array($without_i,array()); // and return it
- }
- else
- {
- // Get the result of the next branch (WITH this one picked,so available weight is reduced)
- list ($with_i,$with_PI) = knapSolveFast2($itemWeight,($i-1),($availWeight - $itemWeight[$i]),$pickedItems);
- $with_i += $itemValue[$i]; // ..and add the value of this one..
- // Get the greater of WITH or WITHOUT
- if ($with_i > $without_i)
- {
- $res = $with_i;
- $picked = $with_PI;
- array_push($picked,$i);
- }
- else
- {
- $res = $without_i;
- $picked = $without_PI;
- }
- $memoItems[$i][$availWeight] = $res; // Store it in the memo
- $memoItems['picked'][$i][$availWeight] = $picked; // and store the picked item
- return array ($res,$picked); // and then return it
- }
- }
- }
- $items = array("map","compass","water","sandwich","glucose","tin","banana","apple","cheese","beer","suntan cream","camera","t-shirt","trousers","umbrella","waterproof trousers","waterproof overclothes","note-case","sunglasses","towel","socks","book");
- $weight = array(9,13,153,50,15,68,27,39,23,52,11,32,24,48,73,42,43,22,7,18,4,30);
- $value = array(150,35,200,160,60,45,40,30,10,70,75,80,20,12,10);
- ## Initialize
- $numcalls = 0;
- $memoItems = array();
- $selectedItems = array();
- ## Solve
- list ($m4,$selectedItems) = knapSolveFast2($weight,$value,sizeof($value)-1,400,$selectedItems);
- # Display Result
- echo "<b>Items:</b><br>" . join(",",$items) . "<br>";
- echo "<b>Max Value Found:</b><br>$m4 (in $numcalls calls)<br>";
- echo "<b>Array Indices:</b><br>". join(",$selectedItems) . "<br>";
- echo "<b>Chosen Items:</b><br>";
- echo "<table border cellspacing=0>";
- echo "<tr><td>Item</td><td>Value</td><td>Weight</td></tr>";
- $totalValue = 0;
- $totalWeight = 0;
- foreach($selectedItems as $key)
- {
- $totalValue += $value[$key];
- $totalWeight += $weight[$key];
- echo "<tr><td>" . $items[$key] . "</td><td>" . $value[$key] . "</td><td>".$weight[$key] . "</td></tr>";
- }
- echo "<tr><td align=right><b>Totals</b></td><td>$totalValue</td><td>$totalWeight</td></tr>";
- echo "</table><hr>";
- ?>
在Python(对不起,这是我的脚本语言的选择),一个强力的解决方案可能看起来像这样.首先,有一个功能,用于生成所有的子集与宽度优先搜索(这很重要).
- def all_subsets(S): # brute force
- subsets_so_far = [()]
- for x in S:
- new_subsets = [subset + (x,) for subset in subsets_so_far]
- subsets_so_far.extend(new_subsets)
- return subsets_so_far
那么有一个函数返回True,如果解决方案是有效的(在预算范围内,并且具有适当的位置分解) – 调用is_valid_solution – 并给出一个解决方案返回总玩家价值(total_player_value)的函数.假设玩家是可用玩家的名单,这是最佳的解决方案.
- max(filter(is_valid_solution,all_subsets(players)),key=total_player_value)
现在,对于DP,我们向all_subsets添加一个函数cull.
- def best_subsets(S): # DP
- subsets_so_far = [()]
- for x in S:
- new_subsets = [subset + (x,) for subset in subsets_so_far]
- subsets_so_far.extend(new_subsets)
- subsets_so_far = cull(subsets_so_far) ### This is new.
- return subsets_so_far
什么剔除是剔除在我们寻求最佳解决方案中显然不会错过的部分解决方案.如果部分解决方案已经超过预算,或者如果已经在一个位置上拥有太多的玩家,那么它可以安全地被丢弃.让is_valid_partial_solution是一个测试这些条件的函数(它可能看起来很像is_valid_solution).到目前为止,我们有这个.
- def cull(subsets): # INCOMPLETE!
- return filter(is_valid_partial_solution,subsets)
另一个重要的测试是,一些部分解决方案比别人更好.如果两个部分解决方案具有相同的位置分解(例如,两个前进和中心)并且成本相同,那么我们只需要保持更有价值.让cost_and_position_breakdown取一个解决方案,并产生一个编码指定属性的字符串.
- def cull(subsets):
- best_subset = {} # empty dictionary/map
- for subset in filter(is_valid_partial_solution,subsets):
- key = cost_and_position_breakdown(subset)
- if (key not in best_subset or
- total_value(subset) > total_value(best_subset[key])):
- best_subset[key] = subset
- return best_subset.values()
而已.这里有很多优化(例如,丢弃部分解决方案,其中有一个更便宜和更有价值的部分解决方案;修改数据结构,使我们不总是从头开始计算价值和位置分解,并减少存储成本),但可以逐步解决.