昨日看到了两道面试题,有两道,第一道不少人都答出来了,第二道却鲜有人回答。我本人最近在学习php,因此本文以php为基础带来今天带来第二道的分析。php
附两道面试题:面试
1:大厅里有100盏灯,每盏灯都编了号码,分别为1-100。每盏灯由一个开关来控制。(开关按一下,灯亮,再按一下灯灭。开关的编号与被控制的灯相同。)开始时,灯是全灭的。如今按照如下规则按动开关。
第一次,将全部的灯点亮。
第二次,将全部2的倍数的开关按一下。
第三次,将全部3的倍数的开关按一下。
以此类推。第N次,将全部N的倍数的开关按一下。
问第100次按完之后,大厅里还有几盏灯是亮的。
2:有一根27厘米的细木杆,在第3厘米、7厘米、11厘米、17厘米、23厘米这五个位置上各有一只蚂蚁。木杆很细,不能同时经过一只蚂蚁。开始时,蚂蚁的头朝左仍是朝右是任意的,它们只会朝前走或调头,但不会后退。当任意两只蚂蚁碰头时,两只蚂蚁会同时调头朝反方向走。假设蚂蚁们每秒钟能够走一厘米的距离。编写程序,求全部蚂蚁都离开木杆的最小时间和最大时间。数组
第一道比较简单很少说了,第二道看着就让人头疼。函数
简单分析一下这道题。学习
从题自己来看,貌似同时考虑五个蚂蚁的位置着实让人摸不着头脑。所幸题的最后一句仍是颇有用的,全部蚂蚁都离开木杆的最大时间和最小时间。将细杆做为一个横向的坐标轴。蚂蚁位置都已经给出。当最后离开的蚂蚁的位置<=0或者>=27的时候,全部蚂蚁离开木杆。(这貌似是废话。)spa
每秒1米,这样的题设足够让人舒服。毕竟在此处蚂蚁运动的时间数值上等于蚂蚁运动的路程的数值。(若是考虑全部蚂蚁离开木杆还继续保持原有速度运动的话)。而且它们是同时运动。code
蚂蚁的运动方向只有两个,向左或向右。考虑到坐标轴的实际状况,若是咱们假设向右移动为1,那么等价向左移动为-1。在计算机的二元世界这一步考虑是很是重要的。blog
好了,前面是铺垫,无论您看懂看不懂,下面是更加剧点的内容。字符串
求最大时间和最小时间,就像咱们在一堆数里面找最大数和最小数同样,这种事情应该不是很难。关键是找到最后作比较的时间。 时间和什么有关系?从题设来看,只应该和初始每只蚂蚁的运动状态有关。至于期间某个时刻某只蚂蚁的运动状态咱们有必要纠结么?不必。那样只会将问题复杂化。get
蚂蚁初始状态有几种?2^5=32种。显然这32种每种花费时间都要算,利用一个简单的循环就能够了。
关注蚂蚁运动状态无非两个变量:位置和方向。于是在此处我简单引入两个数组 $arr和$b 。前者用来描述某个点的当前位置,后者用来当前方向。 $b[i]
如前面所描述的,取值只应该是-1或者1。
考虑到这里,咱们就能够捋顺思路,给数组'$arr'和'$b'赋予一个初始值。利用时间'$i'作循环,每一秒每只蚂蚁移动后,当'$arr[$k]==$arr[$k-1]'时,改变相匹配的状态值'$b[k]'的值。 当全部的'$arr'的'value'<=0或者>=27时,中止循环,返回'$i'。其中用了大量的循环遍历。固然为了简便,当'$arr[$k]'再也不杆子上时,能够利用unset()函数将其删除。最后判断'$arr'为空就能够结束循环。
讲完主体内容,咱们还必须处理一个小细节,如何生成描述蚂蚁运动状态的数组"$b"?
总不能手动生成吧,5只蚂蚁32种状况,10只蚂蚁1024种状况手动生成着实蛋疼。但你明明又知道生成32个数组,不能不利用。于是咱们很容易想到将十进制数转化为长度为5的二进制数。再将这个二进制数中的0替换为-1。将替换后的字符串转化为数组。
贴上相应代码:
<?php for($j=0;$j<32;$j++){ $var=sprintf("%05b", $j); $var=str_replace('1', '1|', $var); $var=str_replace('0', '-1|', $var); $b=explode('|',$var); $res=getRes($b); if (isset($min)) { if ($res<$min) { $min=$res; } }else{ $min=$res; } if (isset($max)) { if ($res>$max) { $max=$res; } }else{ $max=$res; } print_r($b); echo "这次结果是".$res.' $max='.$max.' $min='.$min; echo "<hr/>"; } echo "最大值是".$max."最小值是".$min; //得到某种状况下的时间 function getRes($b){ $arr=array(3,7,11,17,23); for($i=1;$i<100;$i++){ foreach ($arr as $k => $val) { $arr[$k]=$val+$b[$k]; if ($arr[$k]==@$arr[$k-1]) { $b[$k]=-$b[$k]; $b[$k-1]=-@$b[$k-1]; } if (($arr[$k]>=27)||($arr[$k]<=0)) { unset($arr[$k]); } } if (empty($arr)) { return $i; } } }
这是按套路出牌的,循规蹈矩,一步一步走过来,但确实也十分辛苦。
------------------------------------- ---华丽的分隔线-------------------------------------------------------------------------------------------
在我一本正经地胡说八道后,就没有更加好的想法?
那就是相遇的时候,两只蚂蚁开始掉头。若是不掉头直接走呢?和他们掉头后有什么差异?结果是没有差异!每只蚂蚁开头拿一个接力棒,碰头后,两人交换接力棒,虽然蚂蚁掉头了,但接力棒但是一直往初始方向走哦~因此解题前景变得无比明朗。知道某只蚂蚁的初始状态,就知道他开始拿的接力棒最后走了多久!至于接力棒是否是亲生的,那你管哩。反正最后一个接力棒离开杆子,最后一只蚂蚁也离开杆子。
于是得到某种初始状态下的时间还能够这样写:
function getRes($b){ $arr=array(3,7,11,17,23); for($i=1;;$i++){ foreach ($arr as $k => $val) { $arr[$k]=$val+$b[$k]; if (($arr[$k]>=27)||($arr[$k]<=0)) { unset($arr[$k]); } } if (empty($arr)) { return $i; } } }
------------------------------------- ---华丽的分隔线-------------------------------------------------------------------------------------------
固然问题能够更加简化,连上面的代码都用不到了。
经过上面分析能够看作每只蚂蚁直接走互不影响。到最后求最大值最小值的时候实际上能够先算出每只蚂蚁到两端的距离。造成五组数字。(3,24),(7,20),(11,16),(10,17),(4,23)在五组数每组数中较小值造成的5个数中最大的一个是最后结果的最小值。 五组数每组数中较大的5个数中最大的那个是结果的最大值。很容易看出来是11和24。为何呢?典型的木桶效应啊。最后一只蚂蚁走出去了才能算完成整个事情。五只蚂蚁所有最短路径出去,获得结果才多是最快的,五只蚂蚁所有最长路径出去,耗时才能是最慢的。
ps:应该不会有更快的想法了吧。
最后感谢@randeng在本问题上的指点~