最快过桥问题—题解

题目:

在漆黑的夜里,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
*/ 
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值