三個水桶等分8升水的問題 -《演算法的樂趣》
智力題目
有三個容積分別為3升、5升、8升的水桶,其中容積為8升的水桶中裝滿了水,容積為3升和容積為5升的水桶都是空的。三個水桶都沒有刻度,現在需要將大水桶中的8升水等分成兩份,每份都是4升水,附加條件是隻能這三個水桶,不能借助其他輔助容器。
“恩,是的,這是一個很經典的問題。”
“然而,我們並不能想全,不信請繼續往下看。”
答案
”廢話不多說,直接看方法吧。“
第一種(7步)
- 將8L的水桶中的水,倒滿5L的水桶,這時:8L水桶為3L、5L水桶為5L、3L水桶為0L
- 將5L的水桶中的水,倒滿3L的水桶,這時:8L水桶為3L、5L水桶為2L、3L水桶為3L
- 將3L的水桶中的水,倒入8L的水桶,這時:8L水桶為6L、5L水桶為2L、3L水桶為0L
- 將5L的水桶中的水,倒入3L的水桶,這時:8L水桶為6L、5L水桶為0L、3L水桶為2L
- 將8L的水桶中的水,倒入5L的水桶,這時:8L水桶為1L、5L水桶為5L、3L水桶為2L
- 將5L的水桶中的水,倒滿3L的水桶,這時:8L水桶為1L、5L水桶為4L、3L水桶為3L
- 將3L的水桶中的水,倒入8L的水桶,這時:8L水桶為4L、5L水桶為4L、3L水桶為0L
第二種(8步)
- 將8L的水桶中的水,倒滿3L的水桶,這時:8L水桶為5L、5L水桶為0L、3L水桶為3L
- 將3L的水桶中的水,倒入5L的水桶,這時:8L水桶為5L、5L水桶為3L、3L水桶為0L
- 將8L的水桶中的水,倒滿3L的水桶,這時:8L水桶為2L、5L水桶為3L、3L水桶為3L
- 將3L的水桶中的水,倒滿5L的水桶,這時:8L水桶為2L、5L水桶為5L、3L水桶為1L
- 將5L的水桶中的水,倒入8L的水桶,這時:8L水桶為7L、5L水桶為0L、3L水桶為1L
- 將3L的水桶中的水,倒入5L的水桶,這時:8L水桶為7L、5L水桶為1L、3L水桶為0L
- 將8L的水桶中的水,倒滿3L的水桶,這時:8L水桶為4L、5L水桶為1L、3L水桶為3L
- 將3L的水桶中的水,倒入5L的水桶,這時:8L水桶為4L、5L水桶為4L、3L水桶為0L
我相信答案肯定不止兩個,到底有多少種答案?
帶著這個疑問,我們來設計一個演算法吧。
問題分析
人的思維
解決這個問題的關鍵是怎麼透過倒水湊出確定的1升水或能容納1升水的空間。
例如,當8L水桶或5L水桶或3L水桶有1L水時,都能快速倒出4L水。
計算機思維
“窮舉法”
水桶初始狀態:8L水桶裝滿水,3L和5L的水桶為空。
水桶最終狀態:3L水桶為空,5L和8L的水桶各4L水。
假設將每個狀態下三個水桶中的水的體積作為status。
從 $status = array(8,0,0) 得到 $status = array(4,4,0)。
當然還會有一些限制:
1.各個水桶的都有最大值:
0 <= status[0] <= 8;
0 <= status[1] <= 5;
0 <= status[2] <= 3;
2.當前倒水之後各個水桶的狀態,與歷史倒水之後各個水桶的狀態,不能相同。
3.當前水桶為空時,不能倒給其他水桶。
4.當前水桶為最大容積時,其他水桶不能再向這個水桶倒水。
程式程式碼(PHP)
/**
* 三個水桶等分8升水的問題,程式碼示例(php)
* @author 訢亮
*/
$bucket_limit = [8, 5, 3];
$bucket_value = [8, 0, 0];
$bucket = new Bucket($bucket_value, $bucket_limit, []);
$result = $bucket->getResult();
echo "一共有 ".count($result)." 種倒水方法,方法如下:<br> <pre>";
print_r($result);
class Bucket
{
static protected $_change_bucket_path = []; //倒水的過程記錄
protected $_bucket_values; //每個水桶的當前容積
protected $_bucket_limit; //每個水桶的容積閾值
protected $_history_status; //所有歷史水桶容積狀態的集合
public function __construct($bucket_value = [], $bucket_limit = [], $history_status = [])
{
$this->_bucket_values = $bucket_value;
$this->_bucket_limit = $bucket_limit;
$this->_history_status = array_merge($history_status, [$this->_bucket_values]);
$this->run();
}
public function run() {
for ($i=0; $i<count($this->_bucket_values); $i++) {
for ($j=$i+1; $j<count($this->_bucket_values); $j++) {
$this->changeBucketValue($i, $j);
$this->changeBucketValue($j, $i);
}
}
}
public function getResult() {
return self::$_change_bucket_path;
}
/**
* 倒水
* @param int $target_idx 目標水桶(被倒水的水桶)
* @param int $current_idx 當前水桶(倒水的水桶)
* @return bool
*/
protected function changeBucketValue($target_idx = 0, $current_idx = 0) {
$value = $this->_bucket_values;
if ($target_idx == $current_idx ||
$this->_bucket_values[$current_idx] == 0 ||
$this->_bucket_values[$target_idx] == $this->_bucket_limit[$target_idx]
) {
return false;
}
if (($this->_bucket_limit[$target_idx] - $this->_bucket_values[$target_idx]) <= $this->_bucket_values[$current_idx]) {
$water = $this->_bucket_limit[$target_idx] - $this->_bucket_values[$target_idx];
} else {
$water = $this->_bucket_values[$current_idx];
}
$value[$target_idx] += $water;
$value[$current_idx] -= $water;
if ($value === [4,4,0]) {
self::$_change_bucket_path[] = array_merge($this->_history_status, [$value]);
} else {
if (!$this->checkBucketStatus($value)) {
new Bucket($value, $this->_bucket_limit, $this->_history_status);
}
}
}
/**
* 驗證當前水桶狀態是否存在過
* @param array $current_status 當前水桶狀態
* @return bool
*/
protected function checkBucketStatus($current_status = []) {
foreach ($this->_history_status as $k) {
if ($current_status === $k) {
return true;
}
}
return false;
}
}
執行結果
一共有 16 種倒水方法,方法如下:
…
(16種方法,貼上去太長了,大家在本地嘗試下。)
小結
執行程式碼之後,一共找到了 16 種倒水的方法,最快的方法需要 7 個步驟。
“怎麼樣,是不是沒想到會有這麼多方法吧,去考考你身邊的小夥伴吧。”
本文歡迎轉發,轉發請註明作者和出處,謝謝!
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2983/viewspace-2824014/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【演算法的樂趣】演算法
- 遊戲,一個有樂趣的、解決問題的過程遊戲
- Java我的三個月——OOP的樂趣 (轉)JavaOOP
- 製作遊戲的遊戲:創作樂趣的樂趣遊戲
- 《演算法的樂趣》,快樂學習演算法,學習貼近生活的演算法演算法
- 推薦網站Project Euler:思考的樂趣&計算的樂趣網站Project
- 【演算法】8皇后問題演算法
- NSError ** 與 throws 的三個問題Error
- Oracle 索引的三個問題(轉)Oracle索引
- 閱讀原始碼的樂趣原始碼
- 重拾 CSS 的樂趣(下)CSS
- 重拾 CSS 的樂趣(上)CSS
- 維護程式的那些樂趣
- 享受工具帶來的樂趣
- jupyter lab 的三個小問題
- 問一個關於oracle8的簡單的問題!Oracle
- 終端中的樂趣:6個有趣的Linux命令列工具Linux命令列
- 擁有一個永遠無法實現的想法的樂趣
- 編碼也快樂:取得3升水C#版C#
- Redis的三個必知必會的問題Redis
- 解決問題的8個步驟-轉載
- 幾個把平面幾何問題的輔助線做到空間去的數學趣題
- 投身投資行業需要思考的8個問題(ZT)行業
- 愛上數學的理由——評《思考的樂趣》
- 資料鏈路層的三個基本問題
- 生活服務app開發的三個問題APP
- 大資料三個重要的技術問題大資料
- 樂趣在於思考
- 享受工作樂趣(轉)
- 多出來的50% Imagination——思考的樂趣
- 現階段Kubernetes架構的8個問題架構
- go的演算法問題Go演算法
- Python網路抓取的三個常見問題Python
- 各位十一好,問一個有關weblogic 8的連線池的問題Web
- 《演算法的樂趣》作者王曉華:“玩”過就是收穫(圖靈訪談)演算法圖靈
- 聚類的基本問題及兩個常用演算法聚類演算法
- Java演算法面試題(007) 蒙提霍爾問題(三門問題)Java演算法面試題
- 引爆你的網頁樂趣!前端十個令人捧腹的JavaScript整蠱程式碼。網頁前端JavaScript