有三個容積分別為8公升、5公升、3公升的水桶,其中容積為8公升的水桶盛滿了水,容積為5公升和3公升的水桶都是空的。三個水桶都沒有刻度,現在需要將水桶中的8公升水等分成2份,每份都是4公升水。條件是只能使用這三個水桶,不能借助其他工具。
對於這個問題,似乎沒有「規律」可循,沒有專門的演算法來求解,因此只能嘗試使用「窮舉法」來求解。
把每個狀態下三個水桶中的水的體積作為status,例如status = array(8,0,0),想要得到的status = array(4,4,0)。
這樣,就把問題轉化為「尋徑」問題了。只需要找到status = array(8,0,0)轉化到status = array(4,4,0)的「路徑」即可。
對於status有如下限制:
0 <= status[0] <= 8;
0 <= status[1] <= 5;
0 <= status[2] <= 3;
在搜尋sub_status的過程中,要注意sub_status不能和current_status和其祖先相等,要不然程式會進入「環路」。一般的,current_status可以得到好幾個sub_status, 如果sub_status和current_status和其祖先的其中乙個相等,則忽略這個sub_status。
一共有三個水桶,則可能的「倒水」動作共有6種(3中取2的全排列),但是在不同的status下,這6種倒水動作中有一些是不可用:①從某乙個桶中倒水,但是這個桶是空的,②向某乙個桶中倒水,但是這個桶已經滿了。
按照上面的分析,可以構造乙個status類:
status 類有四個protected 字段:
_values 值, 例如array(8,0,0)
_limit 限制, 這裡是array(8,5,3)
_children 用來儲存本物件的所有sub_status的values
_ancestors 用來儲存物件本身的values和祖先的values
程式的大體思路是例項化第乙個status——array(8,0,0),在建構函式中搜尋sub_status、篩選sub_status,然後例項化所有的sub_status,如果sub_status等於array(4,4,0)則停止例項化,並將_ancestors輸出,這樣就可以將所有的「路徑」找出。
物件導向風格
1<?php2//
path: document-root/obj/status.php34
class
status520
21protected
function
generatechildren()
2231
if($child !== false && !$this->isancestor($child) && $child !== array(4,4,0))
3435
$child = $this->turnstatus($o, $i
);36
if($child === array(4,4,0))
39if($child !== false && !$this->isancestor($child) && $child !== array(4,4,0))42}
43}44$this->_children = $children;45
}4647protected
function isancestor($value)48
53}54return
false;55
}5657protected
function turnstatus($i, $o)58
6364
if($this->_values[$i] == $this->_limit[$i
])67
if(($this->_limit[$i] - $this->_values[$i]) <= $this->_values[$o
])else
7273
$value[$o] -= $m;74
$value[$i] += $m;75
return
$value;76
}7778protected
function showresult(array
$child)79
86 }
1<?php2//
path: document-root/main.php34
require_once("obj/status.php");
5require_once("register.php");
6echo '';78
$status = new status(array(8, 0, 0), array(8, 5, 3), array
());910
11//
var_dump($status);
1213
$paths = register::registry('final_status');
14printf("共找出 %d 種不同的方式", count($paths
));15
16$stepnum = 0;
17foreach ($paths
as$key => $path
) 21
$_stepnum = count($path
);22
if($_stepnum
< $stepnum)26
}27printf(" 其中,第 %d 種方式所用步數最少,為 %d 步", $_key, $stepnum-1);
28print_r($paths);
1<?php2//
path: document-root/register.php34
class
register512
13static
public
function register($key, $value)14
1718
static
public)19
2223
static
public
function registry($key)24
28return
null;29
}30 }
將上面貼出來的程式按目錄放在**根目錄下,訪問 main.php 即可得到問題解答。
過程化風格:
1<?php2//
file: document-root/index.php
3echo '';45
$limit = array(8,5,3);
6$current = array
(7 'value' => array(8,0,0),
8 'ancestors' => array(9
)10);11
$records = array
();12
$tree = getchildren($current, $limit, $records
);13
print_r($records
);14
1516/**
17* @param $current array('value', 'children', 'ancestors')
18* @param array $limit
19* @param array $records
20* @return array
21*/
22function getchildren($current, array
$limit, array &$records)23
3435
$child = turn($o, $i, $pvalue, $limit
);36
if($child !== false && !isancestor($current, $child
))39}40
}4142foreach ($children
as$key => $child
) else56}
57return
$current;58
}5960/**61
* @param $i
62* @param $o
63* @param $value array
64* @param $limit
65* @return bool|array
66*/
67function turn($i, $o, $value, $limit)68
7273
if($value[$i] == $limit[$i
])76
if(($limit[$i] - $value[$i]) <= $value[$o
])else
8182
$value[$o] -= $m;83
$value[$i] += $m;84
return
$value;85
}8687function isancestor(array
$current, array
$child)88
93return
false
;94 }
在瀏覽器中直接訪問這個檔案即可。
三個容器倒水 三個水桶等分8公升水的問題 演算法的樂趣
有三個容積分別為3公升 5公升 8公升的水桶,其中容積為8公升的水桶中裝滿了水,容積為3公升和容積為5公升的水桶都是空的。三個水桶都沒有刻度,現在需要將大水桶中的8公升水等分成兩份,每份都是4公升水,附加條件是只能這三個水桶,不能借助其他輔助容器。恩,是的,這是乙個很經典的問題。然而,我們並不能想全...
第3 2課 用三個水桶等分 8 公升水的問題
有這樣乙個智力題目 有三個分別是 3 公升 5 公升和 8 公升容積的水桶,其中容積為 8 公升的水桶中裝滿了水,容積為 3 公升和容積為 5 公升的水桶是空的,三個水桶都沒有體積刻度。現在需要把大水桶中的 8 公升水等分成兩份,每份都是 4 公升水,附加條件是只能使用這 8 公升水和另外兩個空水桶...
三個桶等分八公升水
狀態樹的遍歷 有這樣一道智力題目 有三個容積分別是3公升 5公升和8公升的水桶,其中容積為8公升的水桶中裝滿了水,容積為3公升和容積為5公升的水桶是空的。3個水桶都沒有體積刻度,現在需要將大水桶中的8公升水等分成兩份,每份都是4公升水,附加條件是只能使用另外兩個空水桶,不能借助其他輔助容器。這是乙個...