原文链接: 数独问题转精确覆盖问题
上一篇: dfs 求解精确覆盖问题
下一篇: 舞蹈链解精确覆盖问题
精确覆盖问题本质是求解行列式
其中每一列都是一个限制条件, 行是含有解集合的全集, 解是一组行的集合
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
}
}