zoukankan      html  css  js  c++  java
  • 算法笔记-回溯法

      (1)0-1背包问题

         说明:有4个商品,重量分别为2, 5, 4, 2;价值分别为6, 3, 5, 4,背包只能装10重量的物品,怎么装可以获取最大价值的物品?

       思路:构造一个二叉树,每个商品都有两种状态,要或者不要。如果要就在这个节点的左枝挂子节点,如果不要就在右节点挂子节点。如果全部商品都分配完状态之后就回溯,回溯到一个还有其他选择的节点,接着往这个选择发展节点,然后再回溯,然后再往下。。。。  直到无路可走,就结束了。

          假如限制重量是10,总共有四个商品,重量分别是2, 5, 4, 2 价格分别是6, 3, 5, 4。第一轮的路程如5-11图,第1个商品要,第2个商品要,第3个商品发现装不下了,所以到第3个节点只能走右节点,并且第3个节点的左节点成为死节点,没有发展下去的可能了。第4个商品要,此时已经给所有的商品赋予状态了(要或者不要),记录下此时所有商品的价值和,记为最优价格。接着就开始回溯,如5-13,从节点5先是回溯到节点4(此时购物车有1,2,3选择不要,4肯定是要的,所以没必要再发展4节点的右节点),再到节点3(节点三的左节点是死节点),再到节点2,节点2的右节点是可选的,然后接着按照刚开始的逻辑接着往下走就可以了,等继续走完这一轮,计算最优值,更新下最优值,然后再回溯。。。

          剪枝:如果按照上面的逻辑,其实几乎相当于遍历了所有的可能性。如果有4个商品,就会有2的4次方种可能,有些不可能是最优结果的分支直接就剪掉就可以了,比如,如果按照上面的逻辑是会有:1不要2不要3不要4不要这个分支。所以如果发现背包可以把剩下的商品都装入的情况,就直接给剩余的商品赋值为要就可以了。当1不要2不要的时候,3和4可以都装入背包,直接都要就可以了。没必要再走3不要的分支(直接设置成死节点)。或者也可以判断就算把剩余的都加进包里,总价值也小于当前最优值,当前这条路也没必要走了。

                

       代码:这个模板几乎可以解决所有这个类型的问题,buildTree用于构建树,这个微调就可以;thenData用于返回当前选择(这是个抽象概念,该题中表示这个商品要或者不要,部落护卫队问题表示这个人要不要,地图标色问题表示当前国家涂什么颜色)所对应的下一层数据;isContinue用于判断当前路径是否要继续(比如当前路径发展为第一个商品选择装入,第二个商品选择装入,第三个商品选择装入,isContinue将返回false,因为第三个已经装不下了。即使可以装下,有时候我们也可以从其他角度判断返回false,越早的堵死,越少走弯路);travel根据tree返回所有路径,这个方法几乎不用动,根据路径的长度就可以判断是否为完整路径(此题应该与货物数量相等);

     1 <?php
     2 $w = [2, 5, 4, 2];
     3 $v = [6, 3, 5, 4];
     4 $limit = 10;
     5 
     6 $tree = buildTree($w);
     7 print_r(resolve($tree));
     8 
     9 function resolve($obj) {
    10     global $w, $v;
    11     $paths = travel($obj);
    12     $ret = array();
    13     foreach ($paths as $path) {
    14         $valCount = 0;
    15         foreach ($path as $k => $isChoose) {
    16             $valCount += $v[$k] * $isChoose;
    17         }
    18         $ret[$valCount][] = $path;
    19     }
    20     return $ret;
    21     
    22 }
    23 function travel($obj) {
    24     $paths = array();
    25     if (!$obj->nodes) {
    26         return [$paths];
    27     }
    28     
    29     foreach ($obj->nodes as $isChoose => $node) {
    30         $tmpRes = travel($node);
    31         foreach ($tmpRes as & $path) {
    32             $path[$obj->val] = $isChoose;
    33         }
    34         $paths = array_merge($paths, $tmpRes);
    35     }
    36     return $paths;
    37 }
    38 
    39 function buildTree($data, $path = []) {
    40       if (empty($data)) {
    41           return nodeFactory(null);
    42       }
    43     $key = key($data);
    44     $obj = nodeFactory($key);
    45     $nextData = thenData($data);
    46 
    47     foreach ($nextData as $isChoose => $ndata) {
    48         $path[$key] = $isChoose;
    49         if (isContinue($path)) {
    50             unset($ndata[$key]);
    51             $obj->nodes[$isChoose] = buildTree($ndata, $path);
    52         }
    53     }
    54     return $obj;
    55 }
    56 
    57 function thenData($data) {
    58     return  [
    59         1 => $data,
    60         0 => $data,
    61     ];
    62 }
    63 
    64 function isContinue($path) {
    65     global $limit, $w, $v;
    66     static $maxVal = 0;
    67     $weightCount = 0;
    68     $valueCount = 0;
    69     foreach ($path as $k => $value) {
    70         $weightCount += $w[$k] * $value;
    71         $valueCount += $v[$k] * $value;
    72     }
    73     //重量限制
    74     if ($weightCount > $limit) {
    75         return false;
    76     }
    77     
    78     //就算剩下的货物全要, 价值也比不上最大价值
    79     //就没有继续往下的必要了
    80     $curreny = max(array_keys($path));
    81     $remainSumVal = array_sum(array_slice($v, $curreny + 1));
    82     if ($valueCount + $remainSumVal < $maxVal) {
    83         return false;
    84     }
    85     
    86     //更新下最优值
    87     $maxVal = max($maxVal, $valueCount);
    88     return true;
    89 }
    90 
    91 function nodeFactory($val) {
    92     $obj = new stdClass();
    93     $obj->val = $val;
    94     $obj->nodes = array();
    95     return $obj;
    96 }

    ps:这版写的很乱,不要看了 只为记录用

     1 <?php
     2 $w = [2, 5, 4, 2];
     3 $v = [6, 3, 5, 4];
     4 $current = getNode(0);
     5 $count = count($w);
     6 list($limit, $best, $cw, $cp, $bestMap, $map) = [10, 0, 0, 0, array(), array()];
     7 $noBack = true;
     8 
     9 while (1) {
    10     $node = getNode($current->level + 1, $current);
    11     if ($current->level < $count && $noBack) {
    12         if ($best >= array_sum(array_slice($v, $current->level)) + $cp) {
    13             $current->l = false;                                  //剪枝
    14             $current->r = false;
    15             $noBack = false;
    16         }  elseif (is_object($current->l)|| $current->l === false) {
    17             $node->dir = 0;                                        //这种情况是回溯回来的,直接发展右节点就可以了
    18             $current->r = & $node;                                 
    19         } elseif ($cw + $w[$current->level] <= $limit) {
    20             $cw += $w[$current->level];  $cp += $v[$current->level];
    21             $node->dir = 1;                                        //1代表左枝,0代表右枝
    22             $current->l = & $node;                                 //这种情况代表背包可以装下,所以挂在左节点
    23             $map[$current->level] = 1;
    24         } else {
    25             $node->dir = 0;
    26             $current->r = & $node;
    27             $current->l = false;                                   //这种情况代表装不下,左节点是死节点,发展右节点
    28         }
    29         $current = & $node;
    30     } else {                                                       //走完一轮,开始回溯
    31         if ($cp > $best) {                                         //记录最优值
    32             $best = $cp;  $bestMap = $map;
    33         }
    34         while (1) {                                                //开始回溯
    35             $deal = isset($current->dir) ? $current->dir : 0;
    36             $current = & $current->p;        
    37             if ($current === null) {
    38                 break 2;                                          //到头了,结束
    39             }
    40             if (isset($map[$current->level])) {
    41                 unset($map[$current->level]);
    42                 $cw -= $w[$current->level] * $deal;               //怎么加的,怎么减回去
    43                 $cp -= $v[$current->level] * $deal;   
    44             }
    45             if ($current->l === null || $current->r === null) {   //存在活结点
    46                 $noBack = true;
    47                 break;
    48             }
    49         }
    50     }
    51     unset($node);
    52 }
    53 
    54 function getNode($level, & $p = null) {
    55     $node = new stdClass();
    56     $node->level = $level;  $node->p = & $p;
    57     $node->l = null; $node->r = null;
    58     return $node;
    59 }
    60 
    61 print_r(['map' => $bestMap, 'val' => $best]);
    View Code

      (2)最大团问题

       说明

        

       思路:和上一个问题几乎是一样的, 根据场景不同, 我们需要修改isContinue,thenData这两个函数其他的几乎没变

      1 <?php
      2 //每个居民的关系
      3 $map = [
      4     [false, true, true, true, true],
      5     [true, false, true, false, false],
      6     [true, true, false, true, true],
      7     [true, false, true, false, true],
      8     [true, false, true, true, false],
      9 ];
     10 
     11 //五个居民
     12 $residents = [1, 2, 3, 4, 5];
     13 $tree = buildTree($residents);
     14 
     15 print_r(resolve($tree));
     16 
     17 
     18 function resolve($obj) {
     19     global $residents;
     20     $count = count($residents);
     21     
     22     //过滤掉不完成的路径
     23     $paths = array_filter(travel($obj), function ($path) use ($count) {
     24         return count($path) === $count;
     25     });
     26     
     27     //从里面选择一个最大就可以了
     28     $ret = array();
     29     foreach ($paths as $path) {
     30         $count = array_sum($path);
     31         $ret[$count][] = $path;
     32     }
     33     $max = max(array_keys($ret));
     34     return $ret[$max];
     35     
     36 }
     37 function travel($obj) {
     38     $paths = array();
     39     if (!$obj->nodes) {
     40         return [$paths];
     41     }
     42     
     43     foreach ($obj->nodes as $isChoose => $node) {
     44         $tmpRes = travel($node);
     45         foreach ($tmpRes as & $path) {
     46             $path[$obj->val] = $isChoose;
     47         }
     48         $paths = array_merge($paths, $tmpRes);
     49     }
     50     return $paths;
     51 }
     52 
     53 function buildTree($residents, $path = []) {
     54     if (empty($residents)) {
     55         return nodeFactory(null);
     56     }
     57     $cresident = array_shift($residents);
     58     $obj = nodeFactory($cresident);
     59     $nextData = thenData($cresident, $residents);
     60 
     61     foreach ($nextData as $isChoose => $ndata) {
     62         $path[$cresident] = $isChoose;
     63         if (isContinue($path)) {
     64             $obj->nodes[$isChoose] = buildTree($ndata, $path);
     65         }
     66     }
     67     return $obj;
     68 }
     69 
     70 
     71 function isContinue($path) {
     72     global $residents;
     73     static $maxVal = 0;
     74 
     75     //无法超过最大值
     76     $remaind = array_diff($residents, array_keys($path));
     77     if (array_sum($path) + count($remaind) < $maxVal) {
     78         return false;
     79     }
     80     
     81     //更新下最优值
     82     $maxVal = max($maxVal, array_sum($path));
     83     return true;
     84 }
     85 
     86 
     87 function thenData($cresident, $data) {
     88     $choise = array_filter($data, function ($p) use ($cresident){
     89         return isLink($p, $cresident);
     90     });
     91 
     92     //这个把1放在上面可以少走很多弯路1
     93     return  [
     94         1 => $choise,
     95         0 => $data,
     96     ];
     97 }
     98 
     99 function isLink($p1, $p2) {
    100     global $map;
    101     return $map[$p1 - 1][$p2 - 1];
    102 }
    103 
    104 function nodeFactory($val) {
    105     $obj = new stdClass();
    106     $obj->val = $val;
    107     $obj->nodes = array();
    108     return $obj;
    109 }

      (3)地图着色问题

       说明

      

       思路:之前的是二叉树,而这次有多个分支。但总体思路和之前一样

     1 <?php
     2 //每个国家是否相邻
     3 $map = [
     4     [false, true, true, true, false, false, false],
     5     [true, false, true, false, true, false, false],
     6     [true, true, false, true, true, false, false],
     7     [true, false, true, false, true, false, true],
     8     [false, true, true, true, false, true, true],
     9     [false, false, false, false, true, false, true],
    10     [false, false, false, true, true, true, false],
    11 ];
    12 
    13 
    14 //3个颜色
    15 $allColors = [1, 2, 3];
    16 
    17 //7个国家
    18 $countries = [1, 2, 3, 4, 5, 6, 7];
    19 
    20 $tree = buildTree($countries, $allColors);
    21 print_r(resolve($tree));
    22 
    23 function resolve($obj) {
    24     global $countries;
    25     $count = count($countries);
    26     
    27     //过滤掉不完成的路径
    28     $paths = array_filter(travel($obj), function ($path) use ($count) {
    29         return count($path) === $count;
    30     });
    31 
    32     return $paths;
    33     
    34 }
    35 function travel($obj) {
    36     $paths = array();
    37     if (!$obj->nodes) {
    38         return [$paths];
    39     }
    40     
    41     foreach ($obj->nodes as $isChoose => $node) {
    42         $tmpRes = travel($node);
    43         foreach ($tmpRes as & $path) {
    44             $path[$obj->val] = $isChoose;
    45         }
    46         $paths = array_merge($paths, $tmpRes);
    47     }
    48     return $paths;
    49 }
    50 
    51 function buildTree($countries, $colors, $path = []) {
    52     global $allColors;
    53     if (empty($countries)) {
    54         return nodeFactory(null);
    55     }
    56     $country = array_shift($countries);
    57     $obj = nodeFactory($country);
    58 
    59     $nextData = thenData($colors, $countries);
    60     foreach ($nextData as $color => $ndata) {
    61         $path[$country] = $color;
    62         if (isContinue($path)) {
    63             $obj->nodes[$color] = buildTree($ndata, array_diff($allColors, [$color]), $path);
    64         }
    65     }
    66     return $obj;
    67 }
    68 
    69 //这里只要判断所有相邻国家是否有相同着色就可以了
    70 function isContinue($path) {
    71     foreach ($path as $country1 => $color1) {
    72         foreach ($path as $country2 => $color2) {
    73             if (isLink($country1, $country2) && $color1 == $color2) {
    74                 return false;
    75             }
    76         }
    77     }
    78     return true;
    79 }
    80 
    81 
    82 function thenData($colors, $data) {
    83     $ret = array();
    84     $datas = array_fill(0, count($colors), $data);
    85     return array_combine($colors, $datas);
    86 }
    87 
    88 function isLink($p1, $p2) {
    89     global $map;
    90     return $map[$p1 - 1][$p2 - 1];
    91 }
    92 
    93 function nodeFactory($val) {
    94     $obj = new stdClass();
    95     $obj->val = $val;
    96     $obj->nodes = array();
    97     return $obj;
    98 }

      (4)8皇后问题

       说明

         

        

      思路:分支在于判断每个格子是否放置皇后

      1 <?php
      2 
      3 //4个皇后
      4 $queens = 4;
      5 
      6 //所有坐标
      7 $coordinates = array();
      8 for($i=0; $i<$queens; $i++) {
      9     for($j=0; $j<$queens; $j++) {
     10         $coordinates[] = [$i, $j];
     11     }
     12 }
     13 
     14 $tree = buildTree($coordinates);
     15 print_r(resolve($tree));
     16 
     17 function resolve($obj) {
     18     global $coordinates;
     19     $count = count($coordinates);
     20     
     21     //过滤掉不完成的路径
     22     $paths = array_filter(travel($obj), function ($path) use ($count) {
     23         return count($path) === $count;
     24     });
     25 
     26     return $paths;  
     27 }
     28 function travel($obj) {
     29     $paths = array();
     30     if (!$obj->nodes) {
     31         return [$paths];
     32     }
     33     
     34     foreach ($obj->nodes as $isChoose => $node) {
     35         $tmpRes = travel($node);
     36         foreach ($tmpRes as & $path) {
     37             $valToKey = join('-', $obj->val);
     38             $path[$valToKey] = $isChoose;
     39         }
     40         $paths = array_merge($paths, $tmpRes);
     41     }
     42     return $paths;
     43 }
     44 
     45 function buildTree($coordinates, $map = []) {
     46     if (empty($coordinates)) {
     47         return nodeFactory(null);
     48     }
     49     $coord = array_shift($coordinates);
     50     $obj = nodeFactory($coord);
     51 
     52     $nextData = thenData($coordinates);
     53     foreach ($nextData as $isLay => $ndata) {
     54         $tmpMap = $map;
     55         $isLay && $tmpMap[] = $coord;
     56         if (isContinue($tmpMap, $ndata)) {
     57             $obj->nodes[$isLay] = buildTree($ndata, $tmpMap);
     58         }
     59     }
     60 
     61     return $obj;
     62 }
     63 
     64 
     65 function isContinue($map, $ndata) {
     66     global $queens;
     67     
     68     //判断剩余的行数能否放下剩余的皇后 
     69     $remaindQ = $queens - count($map);
     70     $lines = ceil(count($ndata) / $queens);
     71     if ($lines < $remaindQ) {
     72         return false;
     73     }
     74  
     75     //判断是否有两个皇后处于一条直线
     76     foreach ($map as $k1 => $m1) {
     77         foreach ($map as $k2 => $m2) {
     78             if ($k1 != $k2) {
     79                 if ($m1[0] == $m2[0] || $m1[1] == $m2[1] || abs($m1[0] - $m2[0]) == abs($m1[1] - $m2[1])) {
     80                     return false;
     81                 }
     82             }
     83         }
     84     }
     85     return true;
     86 }
     87 
     88 
     89 function thenData($coordinates) {
     90     return [
     91         1 => $coordinates,
     92         0 => $coordinates,
     93     ];
     94 }
     95 
     96 function isLink($p1, $p2) {
     97     global $map;
     98     return $map[$p1 - 1][$p2 - 1];
     99 }
    100 
    101 function nodeFactory($val) {
    102     $obj = new stdClass();
    103     $obj->val = $val;
    104     $obj->nodes = array();
    105     return $obj;
    106 }
    107 

      (5)零件加工问题

       说明

                  

      代码:节点为第几个加工顺序(第1次,第2次。。),分支为加工哪个零件

      

      1 <?php
      2 $m1 = [5, 1, 8, 5, 3, 4];
      3 $m2 = [7, 2, 2, 4, 7, 4];
      4 
      5 $tree = buildTree(array_keys($m1));
      6 print_r(resolve($tree));
      7 
      8 function resolve($obj) {
      9     global $m1;
     10     $count = count($m1);
     11     
     12     //过滤掉不完成的路径
     13     $ret = array();
     14     $paths = travel($obj);
     15     foreach ($paths as $path) {
     16         if (count($path) !== $count) {
     17             continue;
     18         }
     19         $time = celTime(array_reverse($path));
     20         $ret[$time][] = $path;
     21     }
     22     return $ret;  
     23 }
     24 function travel($obj) {
     25     $paths = array();
     26     if (!$obj->nodes) {
     27         return [$paths];
     28     }
     29     
     30     foreach ($obj->nodes as $isChoose => $node) {
     31         $tmpRes = travel($node);
     32         foreach ($tmpRes as & $path) {
     33             $path[$obj->val] = $isChoose;
     34         }
     35         $paths = array_merge($paths, $tmpRes);
     36     }
     37     return $paths;
     38 }
     39 
     40 function buildTree($keys, $path = []) {
     41     global $m1;
     42     if (empty($keys)) {
     43         return nodeFactory(null);
     44     }
     45     
     46     $order = count($m1) - count($keys) + 1;
     47     $obj = nodeFactory($order);
     48     
     49     $nextData = thenData($keys);
     50     foreach ($nextData as $m => $ndata) {
     51         $path[] = $m;
     52         if (isContinue($path)) {
     53             $obj->nodes[$m] = buildTree($ndata, $path);
     54         }
     55         array_pop($path);
     56     }
     57     return $obj;
     58 }
     59 
     60 
     61 function isContinue($path) {
     62     global $m1, $m2;
     63     static $bestVal = PHP_INT_MAX;
     64     
     65     //计算一下时间
     66     $time = celTime($path);
     67     //更新最优值
     68     count($path) == count($m1) && $bestVal = min($time, $bestVal);
     69     return $time <= $bestVal;
     70 }
     71 
     72 function celTime($path) {
     73     global $m1, $m2;
     74     $mTime1 = $mTime2 = [];
     75     foreach ($path as $v) {
     76         $mTime1[$v] = (int)end($mTime1) + $m1[$v];
     77         $begin = empty($mTime2) ? $mTime1[$v] : max(end($mTime2), $mTime1[$v]);
     78         $mTime2[$v] = $begin + $m2[$v];
     79     }
     80     return end($mTime2);    
     81 }
     82 
     83 function thenData($keys) {
     84     $ret = [];
     85     foreach ($keys as $v) {
     86         $ret[$v] = array_diff($keys, [$v]);
     87     }
     88     return $ret;
     89 }
     90 
     91 function isLink($p1, $p2) {
     92     global $map;
     93     return $map[$p1 - 1][$p2 - 1];
     94 }
     95 
     96 function nodeFactory($val) {
     97     $obj = new stdClass();
     98     $obj->val = $val;
     99     $obj->nodes = array();
    100     return $obj;
    101 }

         

  • 相关阅读:
    System.InvalidOperationException: 找到多个与名为“Home”的控制器匹配的类型。
    MemoryStream和FileStream
    js的eval函数解析后台返回的json数据时为什加上圆括号eval("("+data+")"),而HTML页面定义的数据不用
    字符串转数组和数组转字符串
    npoi导入--从varchar数据类型到datetime数据类型转换产生一个超出范围的值问题
    IIS配置 Web部署 svg/woff/woff2字体 404错误
    委托由浅入深学习
    JavaScriptSerializer返回一条Json,页面获取值问题,数据绑定
    一文总结 CPU 基本知识
    Spring原理只要看这篇文章就够了
  • 原文地址:https://www.cnblogs.com/wangjianheng/p/11857435.html
Copyright © 2011-2022 走看看