问题描述
(4)运行结果
在漆黑的夜里,N位旅行者来到了一座狭窄而且没有护栏的桥边。如果不借助手电筒的话,大家是无论如何也不敢过桥去的。不幸的是,N个人一共只带了一只手电筒,而桥窄得只够让两个人同时过。如果各自单独过桥的话,N人所需要的时间已知;而如果两人同时过桥,所需要的时间就是走得比较慢的那个人单独行动时所需的时间。问题是,如何设计一个方案,让这N人尽快过桥。
问题分析
这个问题的解决方案,充分体现了能者多劳(用时短的人必须多跑几次以便传递手电筒),也就是需要用到贪心算法。要么是最快的带最慢的,然后回来带第二慢的,依次带完,要么是最快的两个先过去,然后最快的把手电筒送回来,让最慢的两个过去,然后让第二快的把手电筒送过来。即将过河的人按其过河时间长短从大到小排序,设为对于时间排序得到S(1) <= S(2) .... S(n-1) <= S(n)
先考虑N=1时:
一个人过河时间肯定是S(1)
N = 2时:
时间为 S(2)
N = 3时:
时间则为 S(1)+S(2)+S(3)
N ≥ 4 时:
则有两种情况:
1.第一短的带最长的,再回来带第二短的,依次带完
时间为:(N-2)S(1)+S(2)+······S(n);
2.第一短带第二短,第一短回来,把手电给最长和第二长,再让第二短回来,依次类推
实现需求
(1)确定程序框架
由分析可知,首先把每个人的过河时间存储到数组中再排序,然后通过集合让人和时间一一对应起来,然后统计过桥时间及输出详细的过桥步骤,最后输出总时间。程序框架如以下代码所示:
package com.game_01;
import java.util.Arrays;
import java.util.Scanner;
import java.util.HashMap;
public class CrossBridge {
static HashMap<Integer, String> hm = new HashMap<>();
static Scanner sc = new Scanner(System.in);
static int n = Integer.parseInt(sc.nextLine());
static int[] time = new int[n];
public static void main(String[] args) {
//确定过河的人数
int sum =0;
int a;
System.out.println("过河的人数为" +n);
//将人和速度一一对应起来
for (int i = 0; i < n; i++) {
System.out.println("请输入第"+(i+1)+"个人的过河速度");
time[i] = Integer.parseInt(sc.nextLine());
System.out.println("请输入第"+(i+1)+"个人的姓名");
String name = sc.nextLine();
hm.put(time[i], name);
}
//将过桥时间排序
Arrays.sort(time);
//人数大于等于四人时进行如下循环
for (a = n-1; a > 2; a-=2) {
//最快的两个送最慢的两个过去
if ((time[0] + time[1] + time[1] + time[a]) < (time[0] + time[0] + time[a-1] + time[a])) {
sum = sum + time[0] + time[1] + time[1] + time[a];
step2(0,1,a-1,a);//输出详细过程
}else {
//最快的送最慢的两个过去
sum = sum + time[0] + time[0] + time[a-1] + time[a];
step1(0,1,a-1,a);//输出详细过程
}
}
//人数为三个人时
if (a == 2) {
sum = sum + time[0] + time[1] + time[2];
step3(0,1,a);
}else if (a == 1) {
//人数为两人时
sum = sum + time[a];
step4(a);
}else {
sum = sum + time[0];
}
System.out.println("最短过桥时间为" +sum);
}
(2)完成程序
在原有框架上进行补充就可得到完整的程序
import java.util.Arrays;
import java.util.Scanner;
import java.util.HashMap;
public class CrossBridge {
static HashMap<Integer, String> hm = new HashMap<>();
static Scanner sc = new Scanner(System.in);
static int n = Integer.parseInt(sc.nextLine());
static int[] time = new int[n];
public static void main(String[] args) {
//确定过河的人数
int sum =0;
int a;
System.out.println("过河的人数为" +n);
//将人和速度一一对应起来
for (int i = 0; i < n; i++) {
System.out.println("请输入第"+(i+1)+"个人的过河速度");
time[i] = Integer.parseInt(sc.nextLine());
System.out.println("请输入第"+(i+1)+"个人的姓名");
String name = sc.nextLine();
hm.put(time[i], name);
}
//将过桥时间排序
Arrays.sort(time);
//人数大于等于四人时进行如下循环
for (a = n-1; a > 2; a-=2) {
//最快的两个送最慢的两个过去
if ((time[0] + time[1] + time[1] + time[a]) < (time[0] + time[0] + time[a-1] + time[a])) {
sum = sum + time[0] + time[1] + time[1] + time[a];
step2(0,1,a-1,a);//输出详细过程
}else {
//最快的送最慢的两个过去
sum = sum + time[0] + time[0] + time[a-1] + time[a];
step1(0,1,a-1,a);//输出详细过程
}
}
//人数为三个人时
if (a == 2) {
sum = sum + time[0] + time[1] + time[2];
step3(0,1,a);
}else if (a == 1) {
//人数为两人时
sum = sum + time[a];
step4(a);
}else {
sum = sum + time[0];
}
System.out.println("最短过桥时间为" +sum);
}
//方法一:最快的将最慢的两个送过去
private static void step1(int a, int b, int y, int z) {
//获取人名
String pA = hm.get(time[a]);
String pY = hm.get(time[y]);
String pZ = hm.get(time[z]);
//获取时间
int tA = time[a];
int tY = time[y];
int tZ = time[z];
System.out.println(pA + "和" + pZ + "过桥,花费" + tZ + "分钟");
System.out.println(pA + "回来,花费" + tA + "分钟");
System.out.println(pA + "和" + pY + "过桥,花费" + tY + "分钟");
System.out.println(pA + "回来,花费" + tA + "分钟");
}
//方法二:最快的两个将最慢的两个送过桥
private static void step2(int a, int b, int y, int z) {
//获取人名
String pA = hm.get(time[a]);
String pB = hm.get(time[b]);
String pY = hm.get(time[y]);
String pZ = hm.get(time[z]);
//获取时间
int tA = time[a];
int tB = time[b];
int tZ = time[z];
System.out.println(pA + "和" + pB + "过桥,花费" + tB + "分钟");
System.out.println(pA + "回来,花费" + tA + "分钟");
System.out.println(pY + "和" + pZ + "过桥,花费" + tZ + "分钟");
System.out.println(pB + "回来,花费" + tB + "分钟");
}
//方法三:有三个人过桥的时候
private static void step3(int a, int b, int c) {
System.out.println(hm.get(time[a]) + "和" + hm.get(time[b]) + "过桥,花费" + time[b] + "分钟");
System.out.println(hm.get(time[a]) + "回来,花费" + time[a] + "分钟");
System.out.println(hm.get(time[a]) + "和" + hm.get(time[c]) + "过桥,花费" + time[c] + "分钟");
}
//方法四:有两个人过桥
private static void step4(int a) {
System.out.println(hm.get(time[0]) + "和" + hm.get(time[a]) + "过桥,花费" + time[a] + "分钟");
}
}
(4)运行结果