三個水桶等分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/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 三個水桶等分8升水的問題
- 遊戲,一個有樂趣的、解決問題的過程遊戲
- 製作遊戲的遊戲:創作樂趣的樂趣遊戲
- NSError ** 與 throws 的三個問題Error
- jupyter lab 的三個小問題
- Redis的三個必知必會的問題Redis
- 現階段Kubernetes架構的8個問題架構
- 工業相機使用常見的8個問題
- 資料鏈路層的三個基本問題
- SpringBoot中關於Mybatis使用的三個問題Spring BootMyBatis
- Python網路抓取的三個常見問題Python
- 生活服務app開發的三個問題APP
- Weight for weight JavaScript趣題:減肥俱樂部JavaScript
- 聚類的基本問題及兩個常用演算法聚類演算法
- 問一個 python 演算法題Python演算法
- 關於vuejs的偵聽屬性watch的三個問題VueJS
- MLXG:《三國志·戰略版》赤壁劇本的核心樂趣是做“海王”
- CAD等分不顯示等分點
- 雲風:Factorio (異星工場)的樂趣
- 請教一個演算法問題演算法
- 8個問題看你是否真的懂 JSJS
- 引爆你的網頁樂趣!前端十個令人捧腹的JavaScript整蠱程式碼。網頁前端JavaScript
- 從工作中的一個問題看演算法的重要性演算法
- 請問一個 authorize的問題
- CAD定數等分不顯示等分點
- 能從遠端獲得樂趣的 Linux 命令Linux
- 2019年OTT行業必須面臨的三個問題行業
- 反直覺的三門問題
- 三分鐘看完「分糖果」演算法問題演算法
- 工作三個月問題與思考【前端】前端
- 方法的三個練習題
- 《最強蝸牛》運營分析:這個奇葩放置遊戲的樂趣在哪裡?遊戲
- Java在演算法題中的輸入問題Java演算法
- 一個vuepress配置問題,引發的js遞迴演算法思考VueJS遞迴演算法
- 演算法金 | 線性迴歸:不能忽視的五個問題演算法
- 第九藝術的力量:體驗樂趣,感悟人生
- 由面試題“併發程式設計的三個問題”深入淺出Synchronied面試題程式設計
- 請教個問題執行 httprunner 遇到的問題HTTP