LeetCode 37 解数独

题目:

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则:

数字 1-9 在每一行只能出现一次。

数字 1-9 在每一列只能出现一次。

数字 1-9 在每一个以粗实线分隔的 3×3 宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

示例 1:

输入:


board = [
  ["5","3",".",".","7",".",".",".","."],
  ["6",".",".","1","9","5",".",".","."],
  [".","9","8",".",".",".",".","6","."],
  ["8",".",".",".","6",".",".",".","3"],
  ["4",".",".","8",".","3",".",".","1"],
  ["7",".",".",".","2",".",".",".","6"],
  [".","6",".",".",".",".","2","8","."],
  [".",".",".","4","1","9",".",".","5"],
  [".",".",".",".","8",".",".","7","9"],
]

输出:


[
  ["5","3","4","6","7","8","9","1","2"],
  ["6","7","2","1","9","5","3","4","8"],
  ["1","9","8","3","4","2","5","6","7"],
  ["8","5","9","7","6","1","4","2","3"],
  ["4","2","6","8","5","3","7","9","1"],
  ["7","1","3","9","2","4","8","5","6"],
  ["9","6","1","5","3","7","2","8","4"],
  ["2","8","7","4","1","9","6","3","5"],
  ["3","4","5","2","8","6","1","7","9"],
  ]

解释:输入的数独如上图所示,唯一有效的解决方案如下所示:

提示:

board.length == 9

board[i].length == 9

board[i][j] 是一位数字或者 ‘.’

题目数据 保证 输入数独仅有一个解

解题:

每个行,列,宫里面1-9只出现一次

代码:


func copyMap(elements map[byte]int) map[byte]int {
   tmp := make(map[byte]int, len(elements))
   for k, v := range elements {
      tmp[k] = v
   }
   return tmp
}

func getAMapValue(elements map[byte]int) byte {
   for k := range elements {
      return k
   }
   return '.'
}

func solveMethod(board [][]byte) [][]byte {
   elements := map[byte]int{'1': 1, '2': 1, '3': 1, '4': 1, '5': 1, '6': 1, '7': 1, '8': 1, '9': 1}
   var result [9][9]map[byte]int
   // 行
   for key, line := range board {
      tmp := copyMap(elements)
      for _, v := range line {
         if v != '.' {
            if _, ok := tmp[v]; ok {
               delete(tmp, v)
            }
         }
      }
      for k := range line {
         result[key][k] = copyMap(tmp)
      }
   }
   // 列
   for i := 0; i < 9; i++ {
      tmp := copyMap(elements)
      for _, line := range board {
         if line[i] != '.' {
            if _, ok := tmp[line[i]]; ok {
               delete(tmp, line[i])
            }
         }
      }
      for key := range board {
         for k := range result[key][i] {
            if _, ok := tmp[k]; !ok {
               delete(result[key][i], k)
            }
         }
         if board[key][i] == '.' && len(result[key][i]) == 1 {
            board[key][i] = getAMapValue(result[key][i])
         }
      }
   }
   // 宫
   for i := 0; i < 3; i++ {
      for j := 0; j < 3; j++ {
         tmp := copyMap(elements)
         for m := i * 3; m < i*3+3; m++ {
            for n := j * 3; n < j*3+3; n++ {
               if board[m][n] != '.' {
                  if _, ok := tmp[board[m][n]]; ok {
                     delete(tmp, board[m][n])
                  }
               }
            }
         }
         for m := i * 3; m < i*3+3; m++ {
            for n := j * 3; n < j*3+3; n++ {
               for k := range result[m][n] {
                  if _, ok := tmp[k]; !ok {
                     delete(result[m][n], k)
                  }
               }
               if board[m][n] == '.' && len(result[m][n]) == 1 {
                  board[m][n] = getAMapValue(result[m][n])
               }
            }
         }
      }
   }
   for _, value := range board {
      for _, v := range value {
         if v == '.' {
            board = solveMethod(board)
         }
      }
   }
   return board
}
func solveSudoku(board [][]byte) {
  solveMethod(board)
}

吭哧吭哧写了两天,结果还超时了。。

官方解答:

1.回溯

func solveSudoku(board [][]byte) {
    var line, column [9][9]bool
    var block [3][3][9]bool
    var spaces [][2]int

    for i, row := range board {
        for j, b := range row {
            if b == '.' {
                spaces = append(spaces, [2]int{i, j})
            } else {
                digit := b - '1'
                line[i][digit] = true
                column[j][digit] = true
                block[i/3][j/3][digit] = true
            }
        }
    }

    var dfs func(int) bool
    dfs = func(pos int) bool {
        if pos == len(spaces) {
            return true
        }
        i, j := spaces[pos][0], spaces[pos][1]
        for digit := byte(0); digit < 9; digit++ {
            if !line[i][digit] && !column[j][digit] && !block[i/3][j/3][digit] {
                line[i][digit] = true
                column[j][digit] = true
                block[i/3][j/3][digit] = true
                board[i][j] = digit + '1'
                if dfs(pos + 1) {
                    return true
                }
                line[i][digit] = false
                column[j][digit] = false
                block[i/3][j/3][digit] = false
            }
        }
        return false
    }
    dfs(0)
}

2.位运算


func solveSudoku(board [][]byte) {
    var line, column [9]int
    var block [3][3]int
    var spaces [][2]int

    flip := func(i, j int, digit byte) {
        line[i] ^= 1 << digit
        column[j] ^= 1 << digit
        block[i/3][j/3] ^= 1 << digit
    }

    for i, row := range board {
        for j, b := range row {
            if b == '.' {
                spaces = append(spaces, [2]int{i, j})
            } else {
                digit := b - '1'
                flip(i, j, digit)
            }
        }
    }

    var dfs func(int) bool
    dfs = func(pos int) bool {
        if pos == len(spaces) {
            return true
        }
        i, j := spaces[pos][0], spaces[pos][1]
        mask := 0x1ff &^ uint(line[i]|column[j]|block[i/3][j/3]) // 0x1ff 即二进制的 9 个 1
        for ; mask > 0; mask &= mask - 1 { // 最右侧的 1 置为 0
            digit := byte(bits.TrailingZeros(mask))
            flip(i, j, digit)
            board[i][j] = digit + '1'
            if dfs(pos + 1) {
                return true
            }
            flip(i, j, digit)
        }
        return false
    }
    dfs(0)
}

3.枚举优化

func solveSudoku(board [][]byte) {
    var line, column [9]int
    var block [3][3]int
    var spaces [][2]int

    flip := func(i, j int, digit byte) {
        line[i] ^= 1 << digit
        column[j] ^= 1 << digit
        block[i/3][j/3] ^= 1 << digit
    }

    for i, row := range board {
        for j, b := range row {
            if b != '.' {
                digit := b - '1'
                flip(i, j, digit)
            }
        }
    }

    for {
        modified := false
        for i, row := range board {
            for j, b := range row {
                if b != '.' {
                    continue
                }
                mask := 0x1ff &^ uint(line[i]|column[j]|block[i/3][j/3])
                if mask&(mask-1) == 0 { // mask 的二进制表示仅有一个 1
                    digit := byte(bits.TrailingZeros(mask))
                    flip(i, j, digit)
                    board[i][j] = digit + '1'
                    modified = true
                }
            }
        }
        if !modified {
            break
        }
    }

    for i, row := range board {
        for j, b := range row {
            if b == '.' {
                spaces = append(spaces, [2]int{i, j})
            }
        }
    }

    var dfs func(int) bool
    dfs = func(pos int) bool {
        if pos == len(spaces) {
            return true
        }
        i, j := spaces[pos][0], spaces[pos][1]
        mask := 0x1ff &^ uint(line[i]|column[j]|block[i/3][j/3]) // 0x1ff 即二进制的 9 个 1
        for ; mask > 0; mask &= mask - 1 { // 最右侧的 1 置为 0
            digit := byte(bits.TrailingZeros(mask))
            flip(i, j, digit)
            board[i][j] = digit + '1'
            if dfs(pos + 1) {
                return true
            }
            flip(i, j, digit)
        }
        return false
    }
    dfs(0)
}

学习了

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注