剪枝算法

剪枝,常用于搜索算法的优化,即在在搜索过程中,提前剪去不可能的分支,减少工作量

生活中的常见的一些问题,经常有数量庞大的情况状态组合,无法全部覆盖到。或前继节点状态决定了后继节点的状态不在搜索范围内,这些情况下就应果断剪去无效/非最优分支,留下有效/最优分支

剪枝示意图

九宫格问题

如上图所示为九宫格问题的部分分支扩展,可以看到分支数目极大,随着层数增大复杂度呈几何级数增长

所谓下棋,就是对每一步及之后可能的状态集合进行推演,选择对己方有利的局面,同时根据已经落下的棋子减掉“过期”的分支

1996年DeepBlue vs Kasprov、2016年AlphaGo vs柯洁 ……

剪枝算法没有所谓模板,应具体问题具体分析

实战题目

  1. N皇后问题

    • 思路一:暴力求解,对每一层(行、列)进行简单遍历
    • 思路二:用一维数组对状态进行保存
    • 思路三:DFS深搜
    • 一种DFS

      def solveNQueens(self, n):
      if n<1: return []
      self.result = []
      self.cols = set();self.pie = set(); self.na = set() #na:捺,pie:撇
      self.DFS(n, 0, [])
      return self.generate_result(n)
      def DFS(self, n, row, cur_ _state):
      if row >= n:# recursion terminator
         self. result. append(cur_ state)
         return
      for col in range(n):
         if col in self.cols or row + coL in self.pie or row - col in self.na:
             # go die!
             continue
         # update the flags
      self.cols.add(col)
      self.pie.add(row + col)
      self.na.add(row-col)
      
      self.DFS(n, row + 1, cur_ state + [col])
      
      self.cols.remove(col)
      self.pie.remove(row + col)
      self.na.remove(row - col)
      def _ generate_ result(self, n):
      board=[]
      for res in self.result:
         for i in res:
             board.append("."* i + "Q"+'.'*(n-i-1))
      
      return [board[i: i + n] for i in range(0, len(board), n)]
      
    • 另一种DFS

      def solveNQueens(self, n):
      def DFS(queens, xy_ dif, xy_ sum):
         p = len(queens)
         if p==n:
             result.append(queens)
             return None
         for q in range(n):
      
         if q not in queens and p-q not in xy_ dif and p+q not in xy_sum:
             DFS(queens+[q], xy_dif+[p-q], xy_sum+ [p+q])
      result = []
      DFS([],[],[])
      return [ ["."*i + "Q" + "."*(n-i-1) for i in sol] for sol in result]
      
    • java版本:
      public List<String[]> solveNQueens(int n) {
      List<String []>res = new ArrayList<>();
      helper(0,new boolean[n], new boolean [2*n], new boo lean [2*n], new String[n],res);
      return res;
      }
      private void helper(int r, boolean[] cols, boolean[] d1, boolean[] d2,String[] board, List<String[]> res) {
      if (r == board.length) res.add( board.clone());
      else {
         for (int C = 0; C < board.length; C++) {
             int id1=r-C+board.length,id2=2*board.length-r-C-1;
             if(!cols[c] && !d1[id1] && !d2 [id2]) {
                 char[] row = new char [board. length];
                 Arrays.fill(row, '.'); row[c] = 'Q';
                 board[r] = new String( row);
                 cols[c] = true; d1[id1] = true; d2 [id2] = true;
                 helper(r+1, cols, d1, d2, board, res) ;
                 cols[c] = false; d1[id1] = false; d2[id2] = false;
             }
         }
      }
      }
      
  2. N皇后问题-2

  3. 有效数独 所谓“有效数独”,指的是col[9]、row[9]、block[3][3]内不存在重复数字。和N皇后问题类似,大致思路也是搜索+剪枝,关键问题在于如何对数独中的元素进行有效地标记和判重

    • 思路:两重for循环,统计行row[9]、列clo[9]以及小九格block[3][3](9*9棋盘)中每个数字出现次数,判断是否出现重复。其中,小九格的映射关系是block[i/3][j/3],row、col可设计为二维数组,第二维表示1-9的数字出现次数,也可设计为Set数组。
  1. 解数独
    整体思路:将空格一个一个试着填入数字,发现冲突无解时再退回重新填
    • 基本思路:DFS对所有空格(j,j)进行枚举,每次枚举过程:
      • 先判断j+1是否超限(超出右边界),若超限则i+=1,j=0,从下一行开始
      • 再遍历1-9的数,若该数字有效(row[i]、col[j]、block[i/3][j/3]内均无重复的数)则填入,下一个空格
      • 若1-9的数字都无效,则回退上一个空格,填入该空格下一个有效的数 加速思路:
    • 先从那些已经确定较多的行,即选项少的空格入手,确定下来后总体扩展空间就小得多
    • 遍历之前先做预处理,将每个空格处的有效数字先计算出来,遍历时直接从有效数字里面遍历即可。
    • 使用高级的数据结构,比如dancing link
    • 使用位运算判重……
      • 简单行列遍历基础实现:
        class Solution {
        public void solveSudoku(char[][] board) {
        if (board == null || board.length == 0) return;
        solve(board);
        }
        public boolean solve(char[][] board){
        for (int i = 0; i < board.length; i++) {
           for (int j = 0; j < board[0].length; j++) {
               if (board[i][j]=='.') {
                   for(char c='1';c<='9';c++){
                       if (isValid(board, i, j, c)) {
                           board[i][j] = c;
                           if (solve(board)) return true;
                           else board[i][j] = '.'; //Otherwise go back
                           }
                   }
               return false;
               }
           }
        }
        return true;
        }
        private boolean isValid( char[][] board,int row,int col, char c){
        for(int i = 0; i<9; i++){
           if(board[i][col] != '.' && board[i][col] == c) return false;// check row
           if(board[row][i] != '.' && board[row][i] == c) return false;//check column
           if(board [3 * (row / 3) +i / 3][ 3 * (col / 3)+i % 3] != '.'
               && board [3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] == c) return false;//check 3*3 block  
        }
        return true;
        }
        }
        

results matching ""

    No results matching ""