三个水桶等分8升水的问题

问题描述:

有三个容积分别为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输出,这样就可以将所有的“路径”找出。

程序示例(php):

面向对象风格

 1 <?php
 2 //path: document-root/obj/Status.php
 3 
 4 class Status
 5 {
 6     protected $_values;
 7     protected $_limit;
 8     protected $_children;
 9     protected $_ancestors;
10 
11     public function __construct(array $values, array $limit, array $ancestors)
12     {
13         $this->_values = $values;
14         $this->_limit = $limit;
15         $ancestors[] = $this->_values;
16         $this->_ancestors = $ancestors;
17 
18         $this->generateChildren();
19     }
20 
21     protected function generateChildren()
22     {
23         $children = array();
24 
25         for($i=0;$i<count($this->_values);$i++){
26             for($o=$i+1;$o<count($this->_values);$o++){
27                 $child = $this->turnStatus($i, $o);
28                 if($child === array(4,4,0)){
29                     $this->showResult($child);
30                 }
31                 if($child !== false && !$this->isAncestor($child) && $child !== array(4,4,0)){
32                     $children[] = new Status($child, $this->_limit, $this->_ancestors);
33                 }
34 
35                 $child = $this->turnStatus($o, $i);
36                 if($child === array(4,4,0)){
37                     $this->showResult($child);
38                 }
39                 if($child !== false && !$this->isAncestor($child) && $child !== array(4,4,0)){
40                     $children[] = new Status($child, $this->_limit, $this->_ancestors);
41                 }
42             }
43         }
44         $this->_children = $children;
45     }
46 
47     protected function isAncestor($value)
48     {
49         foreach ($this->_ancestors as $ancestor) {
50             if($value === $ancestor){
51                 return true;
52             }
53         }
54         return false;
55     }
56 
57     protected function turnStatus($i, $o)
58     {
59         $value = $this->_values;
60         if($this->_values[$o] == 0){
61             return false;
62         }
63 
64         if($this->_values[$i] == $this->_limit[$i]){
65             return false;
66         }
67         if(($this->_limit[$i] - $this->_values[$i]) <= $this->_values[$o]){
68             $m = $this->_limit[$i] - $this->_values[$i];
69         }else{
70             $m = $this->_values[$o];
71         }
72 
73         $value[$o] -= $m;
74         $value[$i] += $m;
75         return $value;
76     }
77 
78     protected function showResult(array $child)
79     {
80         $path = $this->_ancestors;
81         $path[] = $child;
82         //print_r($path);
83         Register::append('final_status', $path);
84         //echo "<br/>";
85     }
86 }
 1 <?php
 2 //path: document-root/main.php
 3 
 4 require_once("obj/Status.php");
 5 require_once("Register.php");
 6 echo '<pre>';
 7 
 8 $status = new Status(array(8, 0, 0), array(8, 5, 3), array());
 9 
10 
11 //var_dump($status);
12 
13 $paths = Register::registry('final_status');
14 printf("共找出 %d 种不同的方式", count($paths));
15 
16 $stepNum = 0;
17 foreach ($paths as $key => $path) {
18     if($key == 0){
19         $stepNum = count($path);
20     }
21     $_stepNum = count($path);
22     if($_stepNum < $stepNum){
23         $stepNum = $_stepNum;
24         $_key = $key;
25     }
26 }
27 printf(" 其中,第 %d 种方式所用步数最少,为 %d 步", $_key, $stepNum-1);
28 print_r($paths);
 1 <?php
 2 //path: document-root/Register.php
 3 
 4 class Register
 5 {
 6     static protected $_data = array();
 7 
 8 
 9     public function __construct()
10     {
11     }
12 
13     static public function register($key, $value)
14     {
15         self::$_data[$key] = $value;
16     }
17 
18     static public function append($key, $value)
19     {
20         self::$_data[$key][] = $value;
21     }
22 
23     static public function registry($key)
24     {
25         if(isset(self::$_data[$key])){
26             return self::$_data[$key];
27         }
28         return null;
29     }
30 }

 将上面贴出来的程序按目录放在网站根目录下,访问 main.php 即可得到问题解答。

 

过程化风格:

 1 <?php
 2 //file: document-root/index.php
 3 echo '<pre>';
 4 
 5 $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 
15 
16 /**
17  * @param $current array('value', 'children', 'ancestors')
18  * @param array $limit
19  * @param array $records
20  * @return array
21  */
22 function getChildren($current, array $limit, array &$records)
23 {
24     $children = array();
25     $pValue = $current['value'];
26 
27     for($i=0;$i<count($pValue);$i++){
28         for($o=$i+1;$o<count($pValue);$o++){
29 
30             $child = turn($i, $o, $pValue, $limit);
31             if($child !== false && !isAncestor($current, $child)){
32                 $children[] = $child;
33             }
34 
35             $child = turn($o, $i, $pValue, $limit);
36             if($child !== false && !isAncestor($current, $child)){
37                 $children[] = $child;
38             }
39         }
40     }
41 
42     foreach ($children as $key => $child) {
43         $current['children'][$key]['value'] = $child;
44 
45         $ancestors = $current['ancestors'];
46         $ancestors[] = $pValue;
47         $current['children'][$key]['ancestors'] = $ancestors;
48 
49         if($child !== array(4,4,0)){
50             $current['children'][$key]['children'] = getChildren($current['children'][$key], $limit, $records);
51         }else{
52             $_records = $ancestors;
53             $_records[] = $child;
54             $records[] = $_records;
55         }
56     }
57     return $current;
58 }
59 
60 /**
61  * @param $i
62  * @param $o
63  * @param $value array
64  * @param $limit
65  * @return bool|array
66  */
67 function turn($i, $o, $value, $limit)
68 {
69     if($value[$o] == 0){
70         return false;
71     }
72 
73     if($value[$i] == $limit[$i]){
74         return false;
75     }
76     if(($limit[$i] - $value[$i]) <= $value[$o]){
77         $m = $limit[$i] - $value[$i];
78     }else{
79         $m = $value[$o];
80     }
81 
82     $value[$o] -= $m;
83     $value[$i] += $m;
84     return $value;
85 }
86 
87 function isAncestor(array $current, array $child)
88 {
89     $ancestors = $current['ancestors'];
90     if(in_array($child, $ancestors)){
91         return true;
92     }
93     return false;
94 }

在浏览器中直接访问这个文件即可。

转载于:https://www.cnblogs.com/jpdoutop/p/5765728.html

容积为15升和27升的两个杯子的最小公倍数为135升,因此可以将水桶的容积设置为135升,这样就可以精确向水桶中注入多少升水了。 要得到52升水,可以按照如下的步骤进行操作: 1. 将15升杯子注满水,将水倒入27升杯子中,此时15升杯子中还剩下12升水,27升杯子中有15升水。 2. 将27升杯子中的水倒入水桶中,此时水桶中有15升水。 3. 将15升杯子中的剩余水倒入27升杯子中,此时15升杯子为空,27升杯子中有27 - 15 = 12升水。 4. 将水桶中的水倒入27升杯子中,此时27升杯子中有15 + 12 = 27升水水桶中剩余的水为15升。 因此,可以得到52升水。 下面是用C++实现的代码: ```c++ #include<iostream> using namespace std; int main() { int cup1 = 15; // 第一个杯子的容积为15升 int cup2 = 27; // 第二个杯子的容积为27升 int bucket = 135; // 水桶的容积为两个杯子容积的最小公倍数 // 得到52升水的操作过程 int water = 52; // 需要注入的水的升数为52升 cout << "需要得到" << water << "升水时的操作过程为:" << endl; // 将15升杯子注满水,将水倒入27升杯子中 int cup1_water = cup1; // 第一个杯子中的水的升数 int cup2_water = 0; // 第二个杯子中的水的升数 cout << "将15升杯子注满水,将水倒入27升杯子中。此时15升杯子中还剩下" << cup1 - cup2_water << "升水,27升杯子中有" << cup2_water << "升水。" << endl; // 将27升杯子中的水倒入水桶中 int bucket_water = cup2_water; // 水桶中的水的升数 cup2_water = 0; // 第二个杯子中的水的升数 cout << "将27升杯子中的水倒入水桶中。此时水桶中有" << bucket_water << "升水。" << endl; // 将15升杯子中的剩余水倒入27升杯子中 cup2_water += cup1_water; cup1_water = 0; cout << "将15升杯子中的剩余水倒入27升杯子中。此时15升杯子为空,27升杯子中有" << cup2_water << "升水。" << endl; // 将水桶中的水倒入27升杯子中 cup2_water += bucket_water; bucket_water = 0; cout << "将水桶中的水倒入27升杯子中。此时27升杯子中有" << cup2_water << "升水水桶中剩余的水为" << bucket_water << "升。" << endl; return 0; } ``` 程序输出结果为: ``` 需要得到52升水时的操作过程为: 将15升杯子注满水,将水倒入27升杯子中。此时15升杯子中还剩下3升水,27升杯子中有12升水。 将27升杯子中的水倒入水桶中。此时水桶中有12升水。 将15升杯子中的剩余水倒入27升杯子中。此时15升杯子为空,27升杯子中有27升水。 将水桶中的水倒入27升杯子中。此时27升杯子中有39升水水桶中剩余的水为0升。 ``` 因此,答案为D.52
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值