五人过河问题的分析

来源:互联网 发布:日本搞笑电影知乎 编辑:程序博客网 时间:2024/05/18 01:46
一、问题描述:
有一家人共5口需要在夜里过河,河上只有一条船,每次最多同时可以有两个人同时过河。由 于在夜中,必须使用手电筒来照明。且目前只有一支手电,并且该手电还可以供电30mins。已知5个人(设为A,B,C,D,E)过河的时间分别为(单 位:min):1、3、6、8、12。求5个人在30mins之内过河的方法。
 
二、一些分析:
       直觉情况下,因为每次必须有人把船摇回来,因此当然是最快的人回来,容易知道,5人过河,需要回来3次,过去4次,若每次都是最快的人回来,那么耗时为3*1+3+6+8+12 = 32mins,显然不是最优解。
       其实正确解法是这样,若用一个3元组(A,B,C)表示A和B过河,C划船回来(C可以是A或者B),那么解法如下:(A,B,A) | (D,E,B) | (A,C,A) | (A,B,X) (时间从小到大顺序为A~E,X表示不必回来)。

这种解法在脑中想想也能得到,下面试着从分析证明的角度分析出这样的解法。首先做几个合理的假设:
1、因为每次只有一个人回来,因此时间耗费是独立的,故每次都是已过河的时间最少的人回来;否则存在更加省时间的解法;
2、因为河有两岸,不能保证过河的人中一定有时间最短的那个,因此考虑时间次短者,其他每次渡河结束时,最短者和次短者不能同时在河的起始岸边。
3、不管如何,用时最快的人肯定要最先过河

于是现在有两种情况:
情况一:只有T1返回
      设5人的时间从小到大为T1~T5,那么若每次返回的都是T1,总共耗时3*T1 + T2 + T3 + T4 + T5,记为Ta。
情况二:若同时考虑T1和T2作为返回者,那么进行如下分析:
A:返回的次数n1 + n2 = 3,且n1,n2均不为零(如果n2为0,变为情况一;如果n1为0,耗时肯定大于情况一,故不考虑);
B:T2返回一次,则下次不能和未过河的人一起过河,因为T1这样做耗时更短;所以结论为T2返回一次,必须过去两个未过河的人(肯定不是T1);
C:关于这两个未过河的人选择,必须是最大者和次大者(题目中为T4,T5),因为若是最大者和其他的人(假设为Y,Y小于T4),那么这三个人过河时间为T5 + T4 > T5 + Y;
D:最后一次必定是最小者和次小者同时过河,因为根据假设2,第n-1次渡河后,最小者和次小者在河两边,于是摆渡回来再过去,最后一次肯定是最小者和次小者。
      
      根据以上分析,最小者和次小者一起渡河两次,因为剩下元素有3个,因此次小者只需回来一次(一次就可以度过两个较大的元素),T1回来两次,因此肯定过去了两次,一次是和T2,一次是和T3,于是总共耗时为 2*T2 + 2*T1 + T2 + T5 + T3 ,记为Tb。

      带入具体数值,Ta = 32, Tb = 29,于是得到了正确的解答。

三、进一步的分析
      到了这里,不妨再分析下为什么会出现3分钟的差距,我们用Ta - Tb得到:
      Ta - Tb = (T1 +T4) - 2*T2,这里可以看出,因为我们用最大者的时间掩盖了次大者的时间,但是也付出了代价,就是次快者的返回比最快者返回更加占用时间,T1+T4可以看做是(T1,T4,T1)的时间,2*T2的时间耗费在一次返回和最后一次和T1过去的时间。根据这个不等式就可以判定是用哪一种方法来过河了。

四、问题扩展
      分析到了这里,可以试着做一次推广,设有n个人,每人耗时Ti(i = 1~n);T1 < T2 <...< Tn,有如下结论:
      必须返回 n-2 次,渡河n-1次(小船容量仍为2人),于是在按照情况一,耗时为
      Ta = (n-2)*T1 + (T2+...+Tn);

       若按照情况2,必至少有T2的一次返回,然后渡过最大者和次大者,且渡过后,根据假设二,最快者和次快者分立两岸,根据假设一,返回后的状态是最快者和次快者在起始岸边,问题变为规模更小的问题(最慢者和次慢着已经过河),于是可以进行下一步判断。

       每次判断的条件为 T1 + T(次慢者) 和 2*T2比较....如果前者小于后者,就用情况一,否则用情况二处理。因为规模的缩小,可以采用递归处理,递归终止条件为人数规模为2或者3,人数规模为2时,返回最慢者的时间,为3时,返回3人时间之和(此时解法唯一,为(A,B,A)(A,C,X))。而每次见小规模的耗时为:
       若为情况一,T2不返回,于是T1来回,得到解法(T1,Tn,T1),耗时Tn+T1;
       若为情况二,T2返回,最大者和次大着过去,得到解法(T1,T2,T2)(Tn-1,Tn,T1),耗时2*T2 + Tn + T1;
 
      于是得到下列递归解法:
    
int boatA(int *t, int n){int *time = t;int i = n;if ( n == 2) return t[1];else if ( n == 3) return sum(time, n);else {if ( 2*time[1] <= time[0] + time[n-2])return time[0]+time[n-1]+2*time[1] + boatA(time, n-2);else return time[0] + time[n-1] + boatA(time, n-1);}}

      实际上可以分析,当有一次判断失败时,后面的也就不用判断了,因为次大者会越来越小,这样可以适当修改上面的程序,下面是题目的非递归算法:
int boatB(int *t, int n){int i = n;int total = 0;if (i == 2) return t[1];if (i == 3) return sum(t, n);while( 2*t[1] <= t[0] + t[i-2]) {total += t[0] + t[i-1] + 2*t[1];i -= 2;}total += (i-2)*t[0] + sum(t+1, i-1);return total;}



       分析难免会有不严密指出,求批评指正..
原创粉丝点击