题目:
在漆黑的夜里,N位旅行者来到了一座狭窄而且没有护栏的桥边。如果不借助手电筒的话,大家是无论如何也不敢过桥去的。不幸的是,N个人一共只带了一只手电筒,而桥窄得只够让两个人同时过。如果各自单独过桥的话,N人所需要的时间已知;而如果两人同时过桥,所需要的时间就是走得比较慢的那个人单独行动时所需的时间。问题是,如何设计一个方案,让这N人尽快过桥。
抽象:
N个人过桥,每个人过桥需要的时间为ARR[i](1<=i<=N).每次最多两个人过桥,并且还要回来一个。(送手电筒),求最快过桥时间。
输入:每人过桥时间数组,人数(数组元素个数)。
输出:最快时间。
解析:
最佳方案构造:
1) N = 1、2 , 所有人直接过桥。 总时间sumTime = ARR[2]
2) N = 3 , 最快的人往返一次把其他两个人送过桥。 总时间sumTime = ARR[1] + ARR[2] + ARR[3]
3) N = 4 , 过桥时间依次递增,4个人分别为: a,b,c,d 。
两种可选方式:
A. 一个人运,由最快的a多次往返送其他人过桥。 A总时间sumTime = 2*ARR[1] + ARR[2] + ARR[3] + ARR[4]
B. 两个人运,由最快的a和次快b过桥,a返回,c和d过桥,b返回。 B总时间sumTime = ARR[2] + ARR[1] + ARR[4] + ARR[2] + ARR[2] = 3*ARR[2] + ARR[1] + ARR[4] ;
综上:总时间sumTime = Math.min(AsumTime, BsumTime);
4) N > 4 , 过桥时间依次递增,分别为:a,b,c,d,e,f ......
可以将N人每次选出4人来过河,这四个人:最快,次快,次慢,最慢。 参考N = 4,比较A总时间、B总时间,取时间最小的。 数学推导即:
若:2*ARR[2] < ARR[1] + ARR[3] 选择两个人运送方式。
2*ARR[2] > ARR[1] + ARR[3] 选择一个人运送方式。
2*ARR[2] = ARR[1] + ARR[3] 以上两种均可。
重复上述,每次选出: 最快,次快,次慢,最慢四人,选择时间小的那种方式运送。
代码:
/**
* @author 慕一春
* @version 1.0.0
* @filename Main.java
* @time 2016年6月14日09:34:28
* @copyright(C) 2016
*/
final public class Main {
public static final int N = 6; // N人
public static final int[]ARR = {0,1,2,5,8,9,10}; // N人过桥耗时
public static int sumTime = 0; // 总耗时
private Main(){};
public static void main(String[] args){
selectCheck(); //选择过河方式
System.out.println(sumTime); //总耗时
}
/**
* 选择过河方式
*/
public static void selectCheck() {
boolean flag = false; // false没有采用单人运, true有单人运
if(N == 1) sumTime = ARR[1];
else if(N == 2) sumTime = ARR[2];
else if(N == 3) sumTime = ARR[1] + ARR[2] + ARR[3];
else{
for(int i = 0; i < (N-2) / 2; i++){ //往返趟数
if (2*ARR[2] < ARR[N - 2*i - 1] + ARR[1]){ //最快2人运
sumTime += ARR[1] + ARR[2]; //最快、次快两人过桥,最快返回
sumTime += ARR[N- 2*i] + ARR[2]; //未过河最慢两人过桥,次快返回
}else{ //接下都让最快一个人运
sumTime += ARR[1] * (N - 2*i - 2); //参考一个人运的总时间: sumTime = (n - 2) * ARR[1] + ARR[2] + ARR[3] + ... + ARR[n];
for (int j = 2; j <= N - 2*i; j++){
sumTime += ARR[j];
}
flag = true;
break;
}
}
// 人数为奇数,并且全部都采用2人运,最后一趟会剩下ARR[1],ARR[2],ARR[3]未过河
if (false == flag && 1 == N % 2){
sumTime += ARR[3] + ARR[2] + ARR[1];
}
// 人数为偶数,并且全部都采用2人运,最后一趟会剩下ARR[1],ARR[2]未过河
if(false == flag && 0 == N % 2){
sumTime += ARR[2];
}
}
}
}
测试数据:
/**
测试数据:
1 2 5 8 ----15
1 2 5 8 9 10 ----30
1 2 5 8 9 10 13 ----40
1 99 101 103 ----305
1、4、5、5、5、8、9 ----40
1 5 6 8 12 34 ----66
*/
测试数据:
1 2 5 8 ----15
1 2 5 8 9 10 ----30
1 2 5 8 9 10 13 ----40
1 99 101 103 ----305
1、4、5、5、5、8、9 ----40
1 5 6 8 12 34 ----66
*/