渡河问题

1. 问题描述:

Description

A group of N people wishes to go across a river with only one boat, which can at most carry two persons. Therefore some sort of shuttle arrangement must be arranged in order to row the boat back and forth so that all people may cross. Each person has a different rowing speed; the speed of a couple is determined by the speed of the slower one. Your job is to determine a strategy that minimizes the time for these people to get across.

Input

The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. Then T cases follow. The first line of each case contains N, and the second line contains N integers giving the time for each people to cross the river. Each case is preceded by a blank line. There won't be more than 1000 people and nobody takes more than 100 seconds to cross.

Output

For each test case, print a line containing the total number of seconds required for all the N people to cross the river.

Sample Input

1
4
1 2 5 10

Sample Output

17

Source

这个问题来自与北大的POJ是1700的一道题目

2. 问题的大概意思是:有一组人数为N的人想渡河,但是每一次一只船只能够运送一个人到对岸,而且需要一个人回来运送其他的人,这些人中速度有快有慢,每一次渡河的时候花费的时间由速度慢的那个人决定,你需要做的是计算出这些人渡河所用的最少时间是多少

输入:第一行输入一个整数,为测试用例的个数,从第二行开始为每个测试用例包含的人数

第三行开始输入每个人的速度,其他的测试用例也像第一个这样输入

输出:所有测试用例的渡河所用的最短时间是多少

3. 因为有了速度的限制我们所以我们不用搜索所有的可能性,即可以不使用dfs来进行解决,如果使用dfs来解决那么时间复杂度会相当高,所以这里我们不使用dfs来进行解决,这种问题也非常地典型,实质上就是贪心的问题,所以我们可以使用贪心的策略来解决这个问题。

确定了使用贪心解决问题之后,我们需要知道怎么样使用贪心的策略来使到求出的解最优化呢,这也是问题的难点所在,也是贪心策略使用的难点所在,因为不知道什么是最优的策略,而且不知道在什么情况下取得最优,但是我们可以通过给出的输入样例去推断出一种方案,像这道题目一样,4个人渡河花费最短时间为17秒

我们可以这样想每一次都是最快的人把较慢的人运送过去,那么其中的过程如下

1   2

1                                                  

1    5

1    

1    10

所用时间为2 + 1 + 5 + 1 + 10 = 19大于17说明这种情况下这种策略是不行的

我们再想出一种另外的方法,那就是使用每一次都是较快的人一起过去,然后最快的人回来,然后较慢的两个人过去然后再较快的人回来,然后较快的两个人过去,那么其中过程如下:

1    2

1

5    10

2

1    2

所用时间为2 + 1 + 10 + 2 + 2 = 17与测试用例相吻合说明这这种策略对于这种情况下是适合的

但是是不是第二种策略就一定适合所有的策略呢,我们可以多举出几个例子来证明自己的观点,可以举出1 2 3 4 来看一下,第一种方案为:

1   2

1                                                  

1    3

1    

1    4

所用时间为2 + 1 + 3 + 1 + 4 = 11

第二种方案为

1   2

1                                                  

3  4

2    

1    2

所用时间为2 + 1 + 4 + 2 + 2 = 11第一种方案与第二种的相同,我们也可以举出其中两者速度中相同的情况,发现有的情况下是第一种的方案优于第二种的方案,说明这个跟测试用例的输入是有关系的,我们不知道在什么情况下哪种方案最优所以我们需要将我们自己选择的较优的两种方案进行比较选择出花费时间较少的那组

因为第一种方案是在循环中是比较好进行处理的,但是第二种方案在循环中是比较难处理的,所以我们需要在一次循环中计算出把两个人送过去所花费的时间,这样每次循环中都是这样处理就容易多了

(第二种方案假如是六个人,八个人...也是一样,每一次把两个最慢和较慢的人送过去,然后较快的两个人回来,运送两个人的时候那么有回到刚开始的状态那么继续运送最慢和较慢的人过去然后一步步地把人送过去)

当岸边的人数为1,2,3的时候进行额外的处理,然后跳出循环即可,最终得到的便是渡河的最短时间

总结一下:贪心的难点是要对具体问题进行分析找出较优的方案,有时候方案只有一种,有时候方案有很多种,很多种的情况下,我们就需要对其进行比较,因为在某些情况下这种策略是最优的,有些情况下,另外一种策略是最优的,通过比较最终得到的肯定是最优的方案

3. 具体的代码如下:

import java.util.Arrays;
import java.util.Scanner;
import static java.lang.Math.min;
public class Main{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt();
        for(int i = 0; i < T; i++){
            int n = sc.nextInt();
            int speed[] = new int[n];
            for(int j = 0; j < n; j++){
                speed[j] = sc.nextInt();
            }
            Arrays.sort(speed);
            solve(n, speed);
        }
            sc.close();
    }

    private static void solve(int n, int[] speed){
        //left为左边的人数3
        int left = n;
        int ants = 0;
        while(left > 0){
            if(left == 1){
                ants += speed[0];
                break;
            }
            if(left == 2){
                ants += speed[1];
                break;
            }
            if(left == 3){
                ants += speed[0] + speed[1] + speed[2];
                break;
            }else{
                int s1 = speed[0] + 2 * speed[1] + speed[left - 1];
                int s2 = 2 * speed[0] + speed[left - 1] + speed[left - 2];
                ants += min(s1, s2);
            }
            left -= 2;
        }
            System.out.println(ants);
    }
}
 

猜你喜欢

转载自blog.csdn.net/qq_39445165/article/details/83660741