五人过河问题的分析

一、问题描述:
有一家人共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;
}



       分析难免会有不严密指出,求批评指正..
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
人羊狼菜过河问题是一个经典的逻辑推理问题,可以使用Java代码进行分析和解决。以下是一个简单的Java代码示例,通过深度优先搜索(DFS)算法来解决该问题。 ```java import java.util.*; public class RiverCrossingProblem { // 定义人羊狼菜过河问题的初始状态 private static final int LEFT = 0; private static final int RIGHT = 1; private static final int[] INIT_STATE = {LEFT, LEFT, LEFT, LEFT}; // 定义人羊狼菜过河问题的目标状态 private static final int[] GOAL_STATE = {RIGHT, RIGHT, RIGHT, RIGHT}; // 定义人、羊、狼、菜的编号 private static final int PERSON = 0; private static final int SHEEP = 1; private static final int WOLF = 2; private static final int VEGETABLE = 3; // 定义人羊狼菜过河问题的状态 private static class State { int[] items; int boat; State parent; public State(int[] items, int boat, State parent) { this.items = items; this.boat = boat; this.parent = parent; } public boolean isGoal() { return Arrays.equals(items, GOAL_STATE); } public List<State> getNextStates() { List<State> nextStates = new ArrayList<>(); for (int i = 0; i < items.length; i++) { if (items[i] != boat) continue; int[] nextItems = items.clone(); nextItems[i] = 1 - nextItems[i]; if (isValidState(nextItems)) { nextStates.add(new State(nextItems, 1 - boat, this)); } for (int j = i + 1; j < items.length; j++) { if (items[j] != boat) continue; int[] nextItems2 = nextItems.clone(); nextItems2[j] = 1 - nextItems2[j]; if (isValidState(nextItems2)) { nextStates.add(new State(nextItems2, 1 - boat, this)); } } } return nextStates; } private boolean isValidState(int[] items) { if (items[SHEEP] == items[WOLF] && items[PERSON] != items[SHEEP]) { return false; } if (items[VEGETABLE] == items[SHEEP] && items[PERSON] != items[VEGETABLE]) { return false; } return true; } } // 使用深度优先搜索算法来解决人羊狼菜过河问题 public static void solve() { Stack<State> stack = new Stack<>(); Set<String> visited = new HashSet<>(); State initState = new State(INIT_STATE, LEFT, null); stack.push(initState); visited.add(Arrays.toString(initState.items)); while (!stack.isEmpty()) { State currentState = stack.pop(); if (currentState.isGoal()) { printPath(currentState); return; } for (State nextState : currentState.getNextStates()) { if (!visited.contains(Arrays.toString(nextState.items))) { stack.push(nextState); visited.add(Arrays.toString(nextState.items)); } } } System.out.println("No solution found!"); } // 打印解路径 private static void printPath(State state) { List<String> path = new ArrayList<>(); while (state != null) { path.add(Arrays.toString(state.items)); state = state.parent; } Collections.reverse(path); for (String s : path) { System.out.println(s); } } public static void main(String[] args) { solve(); } } ``` 在上述代码中,我们首先定义了人羊狼菜过河问题的初始状态和目标状态,以及人、羊、狼、菜的编号。然后,我们定义了一个`State`类来表示人羊狼菜过河问题的状态,包括当前状态下的物品位置、船的位置和父状态。`State`类还包括了判断当前状态是否为目标状态、获取下一步状态的方法以及判断状态是否合法的方法。 接下来,我们使用深度优先搜索算法来解决人羊狼菜过河问题。我们首先定义了一个栈和一个HashSet,用于存储当前搜索路径和已经访问过的状态。然后,我们从初始状态开始搜索,每次从栈中弹出一个状态,并查找该状态下所有可能的下一步状态。如果某个下一步状态是合法的且之前没有访问过,那么将其压入栈中,并将其添加到已访问状态的HashSet中。如果找到了目标状态,则打印解路径;否则,输出“No solution found!”。最后,在`main`方法中调用`solve`方法来解人羊狼菜过河问题。 需要注意的是,上述代码只是一个简单的示例,还有许多可以改进的地方,例如使用广度优先搜索算法、优化搜索过程等等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值