zoukankan      html  css  js  c++  java
  • 算法笔记-贪心算法

    贪心算法:保证每一步都是最优,最终结果肯定是最优的(简化了很多问题,不需要你有大局观)

    场景:求集合的极限值(最多,最短,最便宜等等等...)

    (1)会议安排

      说明:在有限的时间内召开更多的会议,任何两个会议不能有时间冲突。下图是会议详情:

       思路:下一个会议要取结束时间最早的(更优的会议持续时间最短,相当于动态的改变开始时间)

       代码:

     1 <?php
     2 $meetings = [
     3     '1' => [3, 6], '2' => [1, 4], '3' => [5, 7], '4' => [2, 5], '5' => [5, 9],
     4     '6' => [3, 8], '7' => [8, 11], '8' => [6, 10], '9' => [8, 12], '10' => [12, 14],
     5     ];
     6 
     7 //根据结束时间排序
     8 uasort($meetings, function($p1, $p2){
     9     if ($p1[1] == $p2[1]) return 0;
    10     return $p1[1] > $p2[1] ? 1 : -1;
    11 });
    12 
    13 //取不冲突
    14 $last = null;
    15 foreach ($meetings as $k => $meet) {
    16     if (($last === null) || ($last !== null && $meet[0] >= $last))
    17         list($res[], $last) = [$k, $meet[1]];   
    18 }
    View Code

    (2)最短路径

      说明:景区有五个点,求第1个点到其他所有点的最短距离。地图如下:

       思路:依次找最近的点。大致过程如下:

         当前在1:得知1-2路程为2;1-3路程为5 。下面去2和3(因为1的两个出度分别是2和3)

         然后去2:得知2-4路程为6;2-3路程为1;  结合上面新得出的路程:1-3路程为4(1-2-3,要比上面的5小,这里要刷新一下),1-4路程为6(这个分支下面要去3和4)

           然后去3:得知 3-4路程为7;3-5路程为1; 结合上面新得出的路程:1-4路程还是4,1-5路程为5(1-2-3-5)(这个分支下面要去4和5)

          依次类推...

      代码:

     1 <?php
     2 $map = [                                                                        //这里用矩阵表示地图
     3     [null, 2, 5, null, null],
     4     [null, null, 2, 6, null],
     5     [null, null, null, 7, 1],
     6     [null, null, 2, null, 4],
     7     [null, null, null, null, null],
     8 ];
     9 $position = $start = 0;
    10 $been = [];     //去过的点
    11 
    12 function minPath($position)
    13 {
    14     global $map, $start, $been;
    15     static $res = [];                                                           //结果
    16     $been[] = $position;                                                        //记录走过的点
    17     $next = [];
    18     foreach ($map[$position] as $k => $v) {
    19         if ($v !== null) {
    20             if (! isset($res[$start . $k]) || ($res[$start . $k] > $res[$start . $position] + $v)) {
    21                 $res[$start . $k] = $res[$start . $position] + $v;              //记录最短的距离
    22                 if (in_array($k, $been)) $next[] = $k;                          //这里放在后面解释
    23             }
    24             ! in_array($k, $been) && $next[] = $k;                              //记录接下来要走的点
    25         }
    26     } 
    27     
    28     foreach ($next as $v) {                                                     //接着找下个点
    29         minPath($v);
    30     }
    31     return $res;
    32 }
    33 $res = minPath($position);
    View Code

       补充:代码第22行注释:

        刚开始也没发现这个问题,执行上面的案例也没啥问题。但执行下面这个案例就有问题了(假如没有第22行)。走到第2个点的时候,还判定1-2的距离为8,所以计算1-4为9。等后来走到第3个点,把1-2的距离精确到3,但已经晚了。第2个点已经走过一次,不会再走了。也就是说不会再更新1-4的距离。所以当检测出距离更精确的时候(1-3-2比1-2要近),把那个点(第2个点)从记录走过点的数组中移除,重新在走一次。

        或者可以手动遍历一下结果,将需要精确的精确一下,比如当发现1-3-2要比1-2要近,则更新1-2-4(第2个点的所有出度)。但这里要记录路径(只记录1-4=9不行,要记录1-2-4=9),性能应该要比上一个好很多。

      

     (4)最小生成树

      说明:把所有点都连接起来(从一点可以到任意一点),并且所有路径的和最小。地图如下:

      思路:找到与初始点(管它叫a)最近的点(管它叫b),连接ab; 

         找到与a或b最近的点(管它叫c),连接bc(假如c和b最近)

         找到与a或b或c最近的点(管它叫d),连接ad(假如d和a最近)

         一直找完所有的点.....

      代码:

     1 <?php
     2 $map = [                                                                        //要保证其是个连通图
     3     [null, 23, null, null, null, 28, 36],
     4     [23, null, 20, null, null, null, 1],
     5     [null, 20, null, 15, null, null, 4],
     6     [null, null, 15, null, 3, null, 9],
     7     [null, null, null, 3, null, 17, 16],
     8     [28, null, null, null, 17, null, 25],
     9     [36, 1, 4, 9, 16, 25, null],
    10 ];
    11 
    12 function minTree($position)
    13 {
    14     global $map;
    15     $distance = 0; $paths = []; $res[] = $position;
    16     while(true) {
    17         $minLink = ['from' => null, 'name' => null, 'value' => INF];
    18         foreach ($res as $p) {
    19             foreach ($map[$p] as $k => $v) {
    20                 if ($v && $v < $minLink['value'] && ! in_array($k, $res)) {
    21                     $minLink = ['from' => $p, 'name' => $k, 'value' => $v];     //所有已知点遍历一遍,找到离已知点最近的点
    22                 }
    23             }             
    24         }
    25         if ($minLink['name'] !== null) {                                        //收录新的已知点,并记录路径和长度
    26             $res[] = $minLink['name'];
    27             $paths[] = $minLink['from'] . $minLink['name'];
    28             $distance += $minLink['value'];
    29         } else {
    30             break;                                                              //全部走完可以结束了
    31         }
    32     }
    33     
    34     return [$paths, $distance];    
    35 }
    36 
    37 $position = 0;                                                                  //初始点随便选,路径方法可能答案不唯一,但总长度是唯一的
    38 list($paths, $distance) = minTree($position);                                   //57
    View Code

     (5)哈夫曼编码

      说明:根据字母出现的频率,为其分配相应长度的编码(频率越高,编码越短),以下几个字母出现的频率如图:

       思路:把这些字母放到二叉树上,然后用01表示,频率较高的字母靠上。过程如下,不停的取出数组中最小的两个元素加和(和是父节点,这两个元素分别为左右叶)之后再放回数组中,直到剩下最后一个元素:

     

     

     代码:

     1 <?php
     2 $arr = [5, 32, 18, 7, 25, 13];
     3 
     4 function createTree($arr)
     5 {
     6     sort($arr);
     7     $arr[] = INF;                                                               //如果加和之后值在数组中最大,就不会插入了
     8     while (count($arr) > 2) {
     9         $left = gettype($arr[0]) == 'object' ? $arr[0] : newNode($arr[0]);
    10         $right = gettype($arr[1]) == 'object' ? $arr[1] : newNode($arr[1]);     //左右叶
    11         $set = newNode($left->name + $right->name, $left, $right);              //父节点
    12         unset($arr[0], $arr[1]);                                                //去除前两个元素
    13         foreach ($arr as $k => $v)
    14         {
    15             if ($set->name <= $v) {
    16                 array_splice($arr, $k - 2, 0, [$set]);                          //插入加和的元素(每次会重置键值)
    17                 break;
    18             }
    19         }
    20     }
    21     return $arr[0];
    22 }
    23 
    24 function newNode($name, & $left = null, & $right = null)
    25 {
    26     $n = new stdClass();
    27     list($n->name, $n->left, $n->right, $n->parent) = [$name, & $left, & $right, null];
    28     $left && $left->parent = & $n;
    29     $right && $right->parent = & $n;
    30     return $n;
    31 }
    32 createTree($arr);
    View Code

         

      

  • 相关阅读:
    波松分酒问题 C++求最优解. Anthony
    Effective STL 条款7 Anthony
    Effective STL 条款16 Anthony
    数据库设计经验之谈[转载]
    C#递归在dropdownlist显示树状
    多域名同主机js转向
    ASP.NET中常用的26个优化性能方法
    域名解析、子域名、cname别名指向、MX记录、泛域名与泛解析、域名绑定、域名转向
    C#数据库递归构造treeview树形结构
    ASP.NET多种方式生成高质量缩略图
  • 原文地址:https://www.cnblogs.com/wangjianheng/p/11697363.html
Copyright © 2011-2022 走看看