zoukankan      html  css  js  c++  java
  • 贪心算法----过河问题

    问题: 

      在漆黑的夜里,N位旅行者来到了一座狭窄而且没有护栏的桥边。如果不借助手电筒的话,大家是无论如何也不敢过桥去的。不幸的是,N个人一共只带了一只手电筒,而桥窄得只够让两个人同时过。如果各自单独过桥的话,N人所需要的时间已知;而如果两人同时过桥,所需要的时间就是走得比较慢的那个人单独行动时所需的时间。问题是,如何设计一个方案,让这N人尽快过桥。

    输入:
      第一行是一个整数T(1<=T<=20)表示测试数据的组数
      每组测试数据的第一行是一个整数N(1<=N<=1000)表示共有N个人要过河
      每组测试数据的第二行是N个整数Si,表示此人过河所需要花时间。(0<Si<=100)
    输出:
      输出所有人都过河需要用的最少时间。
    样例输入:

    1
    4
    1 2 5 10

    样例输出:

    17

    思路分析:

      我们可以根据示例的输出去反推我们选取的策略是不是最优。先假设这四个人是A、B、C、D。那解这道题的一个很自然的想法就是每次让跑得最快的A陪着另一个过桥,然后A快速地跑回来,再陪下一位过去,最后所有人就都可以过桥了。 让我们来算一下这要多长时间。为了方便起见,我们把旅行者出发的桥的这一边称为“此岸”,而把旅行者想要到达的那边叫“彼岸”。在表达一个过桥方案时,我们用“←”来表示从彼岸到此岸的移动,用“→”表示从此岸到彼岸的移动。前面“A护送大家过河”的方案就可以写成:(右边数字为完成此步骤所需时间)。

        A B → 2 
        A ← 1 
        A C → 5 
        A ← 1   
        A D → 10

      所用时间为2 + 1 + 5 + 1 + 10 = 19大于17说明这种情况下这种策略是不行的。但其实有更快的办法:

        A B → 2 
        A ← 1  
        C D → 10            
        B← 2           
        A B → 2 

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

      这个办法的聪明之处在于让两个走得最慢的人同时过桥,这样花去的时间只是走得最慢的那个人花的时间,而走得次慢的那位就不用另花时间过桥了。那这样的策略是不是就一定适合所有的策略呢,我们在这样的情况下就要多举出几个例子来证明自己的观点。我们可以举出 1 2 2 9 来看下,可以发现第一种方案其实是比第二种方案更快。说明这个跟测试用例的输入是有关系的,我们不知道在什么情况下哪种方案最优所以我们需要将我们自己选择的较优的两种方案进行比较选择出花费时间较少的那组。

    代码:

     1 import java.util.Arrays;
     2 import java.util.Scanner;
     3 
     4 public class 快速渡河 {
     5 
     6     public static void main(String[] args) {
     7         Scanner sc = new Scanner(System.in);
     8         int T = sc.nextInt();
     9         for (int i = 0; i < T; i++) {
    10             int n = sc.nextInt();
    11             int[] speed = new int[n];
    12             for (int j = 0; j < n; j++) {
    13                 speed[j] = sc.nextInt();
    14             }
    15             // 排序
    16             Arrays.sort(speed);
    17             f(n, speed);
    18         }
    19     }
    20 
    21     private static void f(int n, int[] speed) {
    22         int left = n;
    23         int ans = 0;
    24         while (left > 0) {
    25             if (left == 1) {// 只有1人
    26                 ans += speed[0];
    27                 break;
    28             } else if (left == 2) {// 只有两人
    29                 ans += speed[1];
    30                 break;
    31             } else if (left == 3) {// 有三人
    32                 ans += speed[2] + speed[0] + speed[1];
    33                 break;
    34             } else {
    35                 // 1,2出发,1返回,最后两名出发,2返回
    36                 int s1 = speed[1] + speed[0] + speed[left - 1] + speed[1];
    37                 // 1,3出发,1返回,1,4出发,1返回,1,2过河
    38                 int s2 = speed[left - 1] + speed[left - 2] + 2 * speed[0];
    39                 ans += Math.min(s1, s2);
    40                 left -= 2;// 左侧是渡河的起点,left代表左侧的剩余人数
    41             }
    42         }
    43         System.out.println(ans);
    44     }
    45 
    46 }

    结果:

      

    总结一下:

      贪心策略简单来说就是出最优的策略,然后再去证明这个策略是否最优,反之换一种策略继续证明,那证明的过程往往是很困难的,我们可以采用反证的方式,然后多举例证明策略是否成立。贪心算法的难点就是选取最优策略,还要判断当前最优是不是整体最。有时候方案只有一种,有时候方案有很多种,很多种的情况下,我们就需要对其进行比较,因为在某些情况下这种策略是最优的,有些情况下,另外一种策略是最优的,通过比较最终得到的肯定是最优的方案。

  • 相关阅读:
    CDQ分治入门
    BSGS算法初探
    简析平衡树(三)——浅谈Splay
    简析平衡树(一)——替罪羊树 Scapegoat Tree
    NOIP2018初赛 解题报告
    【BZOJ1101】[POI2007] Zap(莫比乌斯反演)
    WQS二分学习笔记
    【洛谷2664】树上游戏(点分治)
    同余问题(一)——扩展欧几里得exgcd
    二叉搜索树(BST)学习笔记
  • 原文地址:https://www.cnblogs.com/xiaoyh/p/10356447.html
Copyright © 2011-2022 走看看