4.1 题目描述
牛童骑在牛背上过河,共有甲、乙、丙、丁四头牛,甲牛过河要2分钟,乙牛过河要3分钟,丙牛过河要8分钟,丁牛过河要5分钟,每次只能赶两头牛过河。问:要把四头牛都赶到河对岸去,最少要多少分钟。(提示:牛童回来赶牛过河也得骑在牛背上)。
4.2 程序使用说明
- 输入牛的头数n;
2. 输入每头牛的耗时spend。4.3 分析和设计
- 思路
当只有一头牛时,直接赶过去。当有两头及以上未过河时,一次就需要赶两头牛,如果还有牛未赶过来就需要把一头牛赶回去。以此类推,只要有牛未过河就需要继续赶,在这里运用深度遍历的方法,当所有牛都过河后就等到一个可行解,由于赶牛时有不同的选取方式,这就产生了多个可行解,需要遍历所有解才能找到最优解。
2. 伪代码CrossRiver(left[0...n], right[0...m])// 输入:未过河的牛left,已过河的牛right// 输出:过河最少时间minTime <- sum(left)if n = 1// 只有一头牛Return left[0]for i <- 1 to n dofor j <- i+1 to n do// 选两头牛过河nowTime <- 0// 不能在原位上修改newLeft <- leftNewLeft.remove(j)newLeft.remove(i)newRight <- rightnewRight.add(left[i])newRight.add(left[j])nowTime <- nowTime+max(left[i], left[j])if newLeft.length > 0// 左边还有牛未过河,牧童要回到右边for k <- 1 to newRight.length-1 dotempLeft <- newLefttempLeft.add(newRight[k])tempRight <- newRighttempRight.remove(k)tempTime <- nowTime+newRight[k]// 当前时间加上把剩余的牛驱赶过去的时间就是总时间tempTime <- tempTime + CrossRiver(tempLeft, tempRight)if tempTime < minTimeminTime <- tempTimeelse if nowTime < minTimeminTime <- nowTimereturn minTime
- 时间复杂度
当有n头牛时,从左边任选两头牛最多有n(n-1)/2种方案,即(n-1)+(n-2)+…+1,而从右边选一头牛回来最多有n-1种方案,而整个过程需要n-1个来回,所以时间复杂度为{[n(n-1)/2]*(n-1)}n-1∈O(n3n)4.4 测试用例
| | | | | | | —- | —- | —- | —- | —- | | 1 | n = 4
spend = [2, 3, 8, 5] | 19 | 19 | 通过 | | 2 | n = 5
spend = [3, 5, 8, 7, 6] | 35 | 35 | 通过 | | 3 | N = 2
Spend = [2, 3] | 3 | 3 | 通过 |
4.5 源代码(含注释)
import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.Scanner;public class CrossRiver {int[] spend = new int[]{2, 3, 8, 5};int minTime = Integer.MAX_VALUE;public static void main(String[] args) {System.out.println("请输入牛头数:");Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();System.out.println("请输入"+n+"头牛耗时:");int[] spend = new int[n];for (int i = 0; i < n; i++){spend[i] = scanner.nextInt();}CrossRiver crossRiver = new CrossRiver(spend);crossRiver.crossing();System.out.println("贪心算法结果:"+ crossRiver.minTime);crossRiver.minTime = Integer.MAX_VALUE;List<Integer> list = new ArrayList<>();for (int i = 0; i < crossRiver.spend.length; i++) {list.add(crossRiver.spend[i]);}crossRiver.minTime = crossRiver.crossing(list, new ArrayList<>());System.out.println("遍历算法结果:"+crossRiver.minTime);}public CrossRiver(int[] spend) {this.spend = spend;}public CrossRiver() {}/*** 在1、2、3头牛时,驱赶方式是固定的* 在剩余牛大于等于4时,运用贪心算法的思想,借助最快的把最慢的先赶过去,为此需要先对耗时排序* 有两种方案能较快驱赶最慢的两头牛:* 1. 最快的(即所用时间spend[0])和次快的过河,然后把最快的赶回来,再次慢的和最慢的过河,然后把次快的赶回来.* 即所需时间为:spend[1]+spend[0]+spend[n-1]+spend[1] = spend[0]+2*spend[1]+spend[n-1]* 2. 最快的和最慢的过河,然后把最快的赶回来,再最快的和次慢的过河,然后把最快的赶回来.* 即所需时间为:spend[n-1]+spend[0]+spend[n-2]+spend[0] = 2*spend[0]+spend[n-2]+spend[n-1]*/public void crossing() {// 对花费时间进行排序Arrays.sort(this.spend);int left = this.spend.length;int sum = 0;while (left > 0) {if (left == 1) {sum += spend[0];break;} else if (left == 2) {sum += spend[1];break;} else if (left == 3) {// 先把最慢的赶过去,然后用最快的回来,再把剩余的赶过去sum += spend[2] + spend[0] + spend[1];break;} else {sum += Math.min(spend[0] + 2 * spend[1] + spend[left - 1], 2 * spend[0] + spend[left - 2] + spend[left - 1]);left -= 2;}}minTime = sum;}/*** 深度遍历,先从左边选两个赶到右边,再从右边选一个赶到左边,然后再递归调用,直到左边没有牛* @param left 左边牛状态* @param right 右边牛状态*/public int crossing(List<Integer> left, List<Integer> right) {int minTime = Integer.MAX_VALUE;if (left.size() == 1){return left.get(0);}for (int i = 0; i < left.size(); i++) {for (int j = i + 1; j < left.size(); j++) {int nowTime = 0;// 两两组合从左到右,选择两个移动到右边List<Integer> newLeft = new ArrayList<>(left);newLeft.remove(j);newLeft.remove(i);List<Integer> newRight = new ArrayList<>(right);newRight.add(left.get(i));newRight.add(left.get(j));nowTime += Math.max(left.get(i), left.get(j));if (newLeft.size() > 0) {// 左边有剩余,需要牛童回到左边for (int k = 0; k < newRight.size(); k++) {List<Integer> tempLeft = new ArrayList<>(newLeft);tempLeft.add(newRight.get(k));List<Integer> tempRight = new ArrayList<>(newRight);tempRight.remove(k);int tempNowTime = nowTime+newRight.get(k);// 在当前状态继续驱赶剩余的牛tempNowTime+=crossing(tempLeft, tempRight);if (tempNowTime < minTime){minTime = tempNowTime;}}} else if (nowTime < minTime) {minTime = nowTime;}}}return minTime;}}
