数独问题转精确覆盖问题

原创
10/27 23:13
阅读数 22

精确覆盖问题本质是求解行列式

其中每一列都是一个限制条件, 行是含有解集合的全集, 解是一组行的集合

https://www.cnblogs.com/grenet/p/3163550.html

http://blog.gssxgss.me/use-dlx-to-solve-sudoku-1/

https://www.cnblogs.com/greenty1208/p/9898516.html

 

数独转精确覆盖

dfs 求解精确覆盖太慢了, 尤其是在最后出现冲突的时候, 会将所有前面行的全排列走完才能走到最后, 这种问题确实还是舞蹈链会好点

const INIT_MAP = [
  // 容易
  [0, 9, 0, 6, 0, 0, 3, 0, 0],
  [0, 3, 0, 0, 5, 0, 0, 2, 8],
  [5, 0, 1, 0, 0, 2, 7, 0, 0],
  [0, 0, 6, 1, 0, 8, 0, 0, 2],
  [0, 1, 0, 0, 0, 0, 0, 3, 0],
  [8, 0, 0, 5, 0, 9, 6, 0, 0],
  [0, 0, 9, 7, 0, 0, 4, 0, 3],
  [1, 4, 0, 0, 2, 0, 0, 9, 0],
  [0, 0, 8, 0, 0, 6, 0, 7, 0],
]
// [
//   [1, 2, 5, 3, 6, 4, 9, 7, 8],
//   [9, 4, 6, 8, 2, 7, 1, 5, 3],
//   [7, 8, 3, 1, 5, 9, 2, 4, 6],
//   [2, 5, 4, 6, 8, 1, 3, 9, 7],
//   [6, 1, 9, 2, 7, 3, 4, 8, 5],
//   [3, 7, 8, 4, 9, 5, 6, 2, 1],
//   [5, 3, 2, 7, 4, 6, 8, 1, 9],
//   [8, 9, 1, 5, 3, 2, 7, 6, 4],
//   [4, 6, 7, 9, 1, 8, 5, 3, 2],
// ]

// [
//   //容易
//   [7, 4, 0, 1, 2, 0, 6, 5, 0],
//   [2, 6, 3, 4, 9, 5, 1, 8, 7],
//   [0, 5, 9, 6, 7, 8, 0, 4, 3],
//   [0, 1, 0, 0, 0, 0, 0, 2, 8],
//   [0, 8, 2, 0, 0, 4, 0, 6, 1],
//   [0, 7, 6, 0, 0, 0, 0, 3, 0],
//   [0, 2, 4, 0, 3, 0, 8, 1, 6],
//   [8, 9, 0, 5, 4, 0, 3, 7, 2],
//   [0, 3, 0, 2, 0, 1, 5, 9, 0],
// ]

// [
//   //容易
//   [7, 4, 8, 1, 2, 3, 6, 5, 9],
//   [2, 6, 3, 4, 9, 5, 1, 8, 7],
//   [1, 5, 9, 6, 7, 8, 2, 4, 3],
//   [4, 1, 5, 3, 6, 7, 9, 2, 8],
//   [3, 8, 2, 9, 5, 4, 7, 6, 1],
//   [9, 7, 6, 8, 1, 2, 4, 3, 5],
//   [5, 2, 4, 7, 3, 9, 8, 1, 6],
//   [8, 9, 1, 5, 4, 6, 3, 7, 2],
//   [6, 3, 7, 2, 8, 1, 5, 9, 4],
// ]
//   [
//     //   难
//     [8, 0, 0, 0, 0, 0, 0, 0, 0],
//     [0, 0, 3, 6, 0, 0, 0, 0, 0],
//     [0, 7, 0, 0, 9, 0, 2, 0, 0],
//     [0, 5, 0, 0, 0, 7, 0, 0, 0],
//     [0, 0, 0, 0, 4, 5, 7, 0, 0],
//     [0, 0, 0, 1, 0, 0, 0, 3, 0],
//     [0, 0, 1, 0, 0, 0, 0, 6, 8],
//     [0, 0, 8, 5, 0, 0, 0, 1, 0],
//     [0, 9, 0, 0, 0, 0, 4, 0, 0],
//   ]

const toStr = m => {
  return m.map(line => line.join(",")).join("\n")
}

// 将数独矩阵转换为精确覆盖求解的01矩阵
// 并且返回行对应的解释
const getMap = m => {
  const res = []
  // 行的解释
  const rowMap = {}
  // 行的计数器
  let rowCount = 0
  const width = 81 * 4 // 4个限制条件

  for (let i = 0; i < 9; i++) {
    for (let j = 0; j < 9; j++) {
      const v = m[i][j]
      if (v) {
        const r = Array(width).fill(0)
        // 确定的数字
        // 该点填了数字
        r[i * 9 + j] = 1
        // 第i行有了数字v
        r[81 + i * 9 + v - 1] = 1
        // 第j列有了数字v
        r[81 * 2 + j * 9 + v - 1] = 1
        // 第 i//3 j//3 个 九宫格有了数字v
        const k = Math.floor(i / 3) * 3 + Math.floor(j / 3)
        r[81 * 3 + k * 9 + v - 1] = 1

        // 覆盖矩阵中第i行对应的解
        rowMap[rowCount] = {
          x: i,
          y: j,
          v: v,
        }
        res[rowCount++] = r
      } else {
        // 需要填充的数字
        const s = new Set([
          ...Array(9)
            .fill()
            .map((_, k) => k + 1),
        ])
        for (let w = 0; w < 9; w++) {
          s.delete(m[i][w])
          s.delete(m[w][j])
        }
        // 该行可以设置的值
        for (const w of s) {
          // 该点填了数字
          const r = Array(width).fill(0)
          r[i * 9 + j] = 1
          // 第i行有了数字w
          r[81 + i * 9 + w - 1] = 1
          // 第j列有了数字v
          r[81 * 2 + j * 9 + w - 1] = 1
          // 第 i//3 j//3 个 九宫格有了数字v
          const k = Math.floor(i / 3) * 3 + Math.floor(j / 3)
          r[81 * 3 + k * 9 + w - 1] = 1

          // 覆盖矩阵中第i行对应的解
          rowMap[rowCount] = {
            x: i,
            y: j,
            v: w,
          }
          res[rowCount++] = r
        }
      }
    }
  }

  return [res, rowMap]
}

const [res, resMap] = getMap(INIT_MAP)
console.log(toStr(res))

const { cloneDeep } = require("lodash")

const mtx = res
const width = mtx[0].length
const height = mtx.length
console.log("width,height: ", width, height)

const INIT_STATE = Array(width).fill(0)
for (let i = 0; i < width; i++) {
  for (let j = 0; j < height; j++) {
    INIT_STATE[i] += mtx[j][i]
  }
}

console.log(
  "INIT_STATE",
  INIT_STATE.join("."),
  INIT_STATE.filter(i => i).length,
)
// 矩阵m, 有合计的state, 将第n行添加进ans中
const dfs = (m, n, state, ans, deep = 0) => {
  console.log("dfs", deep, state.filter(i => i).length)
  //   console.log("dfs==2", n, state.join(","), ans.join(","))
  // 所有状态都是1, 说明找到了
  if (state.every(i => i === 1)) {
    // console.log("ans state", state.join(","), ans.join(","))
    return ans
  }
  const row = m[n]
  const newState = cloneDeep(state)
  for (let i = 0; i < width; i++) {
    // 说明该列和已有的冲突了
    if (row[i] && state[i]) {
      // 该列不能加入
      return false
    }
    // 如果放入的行该位置是1, 更新state
    newState[i] |= row[i]
  }
  if (newState.every(i => i === 1)) {
    // console.log("ans state", state.join(","), ans.join(","))
    return [...ans, n]
  }
  for (let i = n + 1; i < height; i++) {
    const res = dfs(m, i, cloneDeep(newState), [...ans, n], deep + 1)
    if (res) return res
  }
}

for (let i = 0; i < height; i++) {
  // 初始状态全部为空
  const ans = dfs(mtx, i, cloneDeep(mtx[i]).fill(0), [])
  if (ans) {
    // console.log("res ans", ans, mtx)
    for (const j of ans) {
      console.log("ans j:", j, resMap[j])
      //   console.log("j", j, mtx[j].join(","))
    }
    return
  }
}

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部