`

代码实现任意容量倒水问题

阅读更多

形象化设计模式实战             HELLO!架构                     redis命令源码解析

 

倒水问题:有两个杯子,一个A升,一个B升,水有无限多,现要求利用这两杯子装C升的水。

 

想必很多人都可能被问到过这个问题,问题虽然简单的,但是要费些脑子。这个问题显然是个逻辑问题,那么就肯定能够用程序来实现。

 

现在我假设A=3,B=5,C=4。

 

大脑的运算过程:

第一步、把3装满,或把5装满(只有这两种选择吧)。

第二步、把装满的杯子的水倒入空杯子(没有人会把第二个杯子也装满吧)。

第三步、两个杯子的水做运算得出一个D(D不能和A、B相等,如果D等于C就成功啦)

第四步、跟第一步相同

第五步、跟第三步相同

直到D等于C就成功,这个要注意的是D的值不能相同,例如第一次运算得到D=2,如果第三次运算又是2,那就不能再继续下去,不然会无限2,够2的。

 

将这个过程转为机器逻辑:两个变量a=3,b=5,要使得变量c=4。这好像太简单了,但是注意的是,杯子容量,假设 3+3=6,但实际上杯子装不了6升的水,换作杯子3+3=1,因为5升杯子装满了,多下一升留在3升杯子中。换作逻辑运行就相当于是取余。

 

代码实现:

<?php
/**
 * 倒水算法
 *
 * @param int     $re    目标水位
 * @param int     $cup1  杯子1的大小
 * @param int     $cup2  杯子2的大小
 * @param int     $i     当前杯子有所装水]
 * @param array   $nocan 不能出现的水位,以免死循环
 * @return int
 */
function todo( $re, $cup1, $cup2, $i, $nocan = array() ) {
	$yu = max( $cup1, $cup2 );
	$arr = array( $cup1, $cup2 );
	if ( !$nocan )
		$nocan = array( 0, $i, $cup1, $cup2 );
	foreach ( $arr as $v ) {
		echo "尝试与{$v}运算\r\n";

		//##########做加法#################
		$tmp = $i + $v;
		echo "{$i}+{$v}\r\n";

		if ( $tmp > $yu ) {
			$tmp = $tmp%$yu;
			echo "取{$yu}的余数{$tmp}\r\n";
		}
		if ( $tmp == $re ) {
			return $tmp;
		}
		if ( !in_array( $tmp, $nocan, true ) ) {
			$nocan[] = $tmp;
			return todo( $re, $cup1, $cup2, $tmp, $nocan );
		}else {
			echo "failed\r\n";
		}

		//#########做减法###################
		$tmp = $i - $v;
		echo "{$i}-{$v}\r\n";
		if ( $tmp > $yu ) {
			$tmp = $tmp%$yu;
			echo "取{$yu}的余数\r\n";
		}
		if ( $tmp == $re ) {
			return $tmp;
		}
		if ( $tmp > 0 && !in_array( $tmp, $nocan ) ) {
			$nocan[] = $tmp;
			return todo( $re, $cup1, $cup2, $tmp, $nocan );
		}else {
			echo "failed\r\n";
		}

		//###########做被减##################
		$tmp = $v - $i;
		echo "{$v}-{$i}\r\n";
		if ( $tmp > $yu ) {
			$tmp = $tmp%$yu;
			echo "取{$yu}的余数\r\n";
		}
		if ( $tmp == $re ) {
			return $tmp;
		}
		if ( $tmp > 0 && !in_array( $tmp, $nocan ) ) {
			$nocan[] = $tmp;
			return todo( $re, $cup1, $cup2, $tmp, $nocan );
		}else {
			echo "failed\r\n";
		}
	}
	return false;
}

 

 

 

现在运行todo(4,3,5,3);就是先将3升杯子装满:

 

 

尝试与3运算
3+3
取5的余数1
尝试与3运算
1+3

 

 

再试下todo(4,3,5,5);先将5升杯子装满:

 

尝试与3运算
5+3
取5的余数3
failed
5-3
尝试与3运算
2+3
failed
2-3
failed
3-2
尝试与3运算
1+3

 

代码问题:

1、做加法,如果与A,B较大数做加法,那么是不是相当于没加?

2、做减法,减与被减用绝对值统一处理即可,abs

3、可能有多种倒水方法,而我最想知道的是最快的那种!

 

优化后代码:

<?php
/**
 * 倒水算法
 *
 * @param int     $re    目标水位
 * @param int     $cup1  杯子1的大小
 * @param int     $cup2  杯子2的大小
 * @param int     $i     当前杯子有所装水]
 * @param array   $nocan 不能出现的水位,以免死循环
 * @param string  $s     倒水过程
 * @return int
 */
function todo( $re, $cup1, $cup2, $i, $nocan = array() , $s ='' ) {
	global $return,$num;
	$yu = max( $cup1, $cup2 );//取较大值
	$arr = array( $cup1, $cup2 );
	if ( !$nocan )
		$nocan = array( 0, $i, $cup1, $cup2 );
	foreach ( $arr as $v ) {
		$str = "";

		//#########做减法###################
		$tmp = abs( $i - $v );//这里直接取绝对值

		$str .= "{$i}与{$v}相减\r\n";
		if ( $tmp > $yu ) {
			$tmp = $tmp%$yu;
			$str .= "取{$yu}的余数\r\n";
		}
		if ( $tmp == $re ) {
			$s .= $str;
			$num[] = array(substr_count( $s, "\r\n" ));
			$return[] = array( substr_count( $s, "\r\n" ), $s );
			return $tmp;
		}
		if ( $tmp > 0 && !in_array( $tmp, $nocan ) ) {
			$nocan[] = $tmp;
			$ss = $s.$str;
			todo( $re, $cup1, $cup2, $tmp, $nocan , $ss );
		}

		$str = "";

		//##########做加法#################
		if ( $yu != $v ) {//跟大杯做加法,相当于白做
			$tmp = $i + $v;
			$str .= "{$i}+{$v}\r\n";

			if ( $tmp > $yu ) {
				$tmp = $tmp%$yu;
				$str .= "取{$yu}的余数{$tmp}\r\n";
			}
			if ( $tmp == $re ) {
				$s .= $str;
				$num[] = array(substr_count( $s, "\r\n" ));
				$return[] = array( substr_count( $s, "\r\n" ), $s );
				return $tmp;
			}
			if ( !in_array( $tmp, $nocan, true ) ) {
				$nocan[] = $tmp;
				$ss = $s.$str;
				todo( $re, $cup1, $cup2, $tmp, $nocan , $ss );
			}
		}
	}
	return false;
}
todo( 4, 43, 77, 77 );
if($num && $return){
	array_multisort($num, SORT_ASC, $return);
	print_r(array_slice($return, 0,2));//方法有很多,只打印两种吧
}else{
	echo "no way!";
}

 

Array
(
    [0] => Array
        (
            [0] => 27
            [1] => 77与43相减
34与43相减
9+43
52+43
取77的余数18
18+43
61+43
取77的余数27
27+43
70+43
取77的余数36
36+43
取77的余数2
2+43
45+43
取77的余数11
11+43
54+43
取77的余数20
20+43
63+43
取77的余数29
29+43
72+43
取77的余数38
38+43
取77的余数4

        )

    [1] => Array
        (
            [0] => 27
            [1] => 77与43相减
34与43相减
9+43
52+43
取77的余数18
18+43
61+43
取77的余数27
27+43
70+43
取77的余数36
36+43
取77的余数2
2+43
45+43
取77的余数11
11+43
54+43
取77的余数20
20+43
63+43
取77的余数29
29与77相减
48与43相减
5与43相减
38+43
取77的余数4

        )

)

 可见最快27步可以搞定!

 

 

最后留一个问题:随意A,B升的水,一定能倒出C升的水的吗?

4
3
分享到:
评论
1 楼 dsjt 2014-11-05  


下载了个应用就是倒水的。

相关推荐

    “倒水”算法代码实现

    用VC控制台实现的倒水程序 “倒水”算法代码实现

    倒水问题的解答

    CSDN 编程大赛 倒水问题,这道题不难,也正是这个原因,我这道题出了些问题,后面改进,可惜却没机会在测试它,因为这题下线了

    三个容器的倒水问题(C语言实现)

    NULL 博文链接:https://touch-2011.iteye.com/blog/1038893

    倒水解密附带源码

    在网上看到这个用C#做的倒水解密游戏,感觉不错,分享一下了。

    java项目_五五开倒水

    java项目_五五开倒水 java项目_五五开倒水 java项目_五五开倒水

    分水问题和倒水问题

    1、 编程解决如下数学问题:有12升水,怎样利用一个8升和一个5升的容器将水分为两个6升?要求以如下格式打印出分水步骤。(20分) a12 b8 c5 12 0 0 * * * ( “*”表示当前状态下每个容器的盛水量) ........

    js实现杯子倒水问题自动求解程序

    智力测试题经常遇到类似的逻辑题,给几个容量不等的杯子,让你倒出多少的水。 安卓上有一款专门玩这个题的游戏叫做Water Logic. 我安装这个游戏把几十个关卡通了一遍,感觉这个游戏的关卡设计很不好,关卡的难度并...

    倒水解密游戏源码2012918

    有N个容量不同的瓶子,指定「将a升水倒入容量为b的瓶子」。 游戏要求通过装水、倒水,达成给定的目标。 游戏操作方式如下: ?在瓶子上双击右键可以把瓶子灌满水 ?双击左键可以把瓶子里的水倒掉 ?将一个瓶子拖动...

    专家系统水壶倒水问题C#程序

    有两个水壶,一个盛满可以4公斤水,另一个3公斤水,水壶没有任何标记。怎样在能装4公斤的水壶里恰好只装2公斤水。使用状态空间法和盲目广度优先搜索算法解决这个问题。

    3d倒水小动画的制作

    自己做的3D倒水是初学者的首选 和容易懂的

    野比的倒水解密游戏的C#的源代码

    野比的倒水解密游戏的C#的源代码,支持脱机玩耍

    OC ioc Xcode三个水杯倒水

    初学IOS做的一个小玩意,8.5.3 三个水杯互相倒水,直至2个水杯都是4

    comfjshmnp-sec.tar

    两个杯子倒水问题,两个版本解决方案,BFS遍历方式,csdn

    野比的倒水解密游戏,纯GDI+,自己DIY游戏

    野比的倒水解密游戏,纯GDI+,自己DIY游戏。

    智能倒水机器人的设计.pdf

    #资源达人分享计划#

    美赛编程实现案例.pdf

    问题描述:有一个杯子,容量为5升。现在有三个桶,分别有2升、3升和4升的水。需要通过桶之间的倒水操作,将杯子中的水量恰好调整为1升。 解决思路:借助三个桶,可以进行倒水操作。首先,我们将2升和4升的桶分别填满...

    物联网家居中具有自动倒水功能的智能饮水机设计

    物联网家居中具有自动倒水功能的智能饮水机设计

    自己写的一些代码

    自己写的一些代码 Gnome Tetravex游戏 Image Perimeters operand提取指定的操作数 左右括号匹配解密 W's Cipher 倒水 踩气球 喝酒

    LeetCode 365水壶问题(python)

    题目描述: 有两个容量分别为 x升 和 y升 的水壶以及无限多...水壶问题就是两个壶的最小公约数与想得到的水的升数是否成倍数问题(除去几个特殊情况,特殊情况在代码中有写到) 当z是最小公约数的整数倍时,可以利用两

Global site tag (gtag.js) - Google Analytics