数独
最近摸鱼的时候玩数独,在leetcode上面也刷数独了,突发奇想自己写一个小游戏。
学到了dlx,真的很优雅的dancing呢
刷题
有效数独
https://leetcode.cn/problems/valid-sudoku/
遍历一次二维数组,行和列简单直接ij调换,九宫格旨在将每行的0-9列转换成每一个ceil的坐标,感觉像是转换成三进制一样👇由i来控制大ceil的坐标,j控制小ceil的坐标
(0,0) --> (0,0) (0,1) --> (0,1) (0,2) --> (0,2)
(0,3) --> (1,0) (0,4) --> (1,1) (0,5) --> (1,2)
(0,6) --> (2,0) (0,7) --> (2,1) (0,8) --> (2,2)
class Solution { public boolean isValidSudoku(char[][] board) { HashSet setx = new HashSet(); HashSet sety = new HashSet(); HashSet setceil = new HashSet(); for (int i = 0 ;i < 9; i ++){ for(int j = 0; j < 9; j ++){ //行 if(board[i][j] != '.' && !setx.add(board[i][j])) return false; //列 if(board[j][i] != '.' && !sety.add(board[j][i])) return false; //九宫格 if(board[(i % 3) * 3 + j / 3][(i / 3) * 3 + j % 3] != '.' && !setceil.add(board[(i % 3) * 3 + j / 3][(i / 3) * 3 + j % 3])) return false; } setx.clear(); sety.clear(); setceil.clear(); } return true; } }
解数独
https://leetcode.cn/problems/sudoku-solver/
如图,第一格为5,故第一行、第一列、第一大格均不能填写5
行列格的set均由000010000标记,标为1表示当前已经存在5;
第二格为3,故第一行,第二列,第一大格均不能填写3
行格set标记为000010100
列set标记为000000100
如此类推,通过9*9遍历压缩空格子填写数字的范围,减少产生n^9还判断是否合理的简单dfs
对于需要填写的地方要满足当前对应的行列格的01串均为0的位置,也就是异或后为0的位置
如图(0,2)的位置或后得到111110100,非一下得到000001011,标为1的地方即为(0,2)可填的数
数独变形
http://poj.org/problem?id=3076
这题真的看了挺久的
DLX模板修改一下。模板是要求每列有且只有一个1(精准覆盖),从行中选择,即行作为决策来覆盖解决列作为的任务。这题的决策就是某行某列填写某个字符即16行*16列*16个字符;任务就是每列填写字母且每列/每行/每个4*4格要有x字母(A-P),构建决策和任务的图是主要需要完成的任务。
16*16*16 每个决策都可进行标号,对于固定字符来说,该行决策只加入一列对应;对于待填写的位置,决策加入A-P所有字符(数独嘛,没有局限都是可填的,只是要找到一种多种决策集合精准覆盖条件,所以都加入)
数独生成
http://47.97.212.226:809/sudo/
https://pan.baidu.com/s/1K3gtXXB1FJMFKm9ArnneaQ?pwd=8888
我的弱智版已经写好了- -
看了几篇博客有些是通过现有数独交换行、列、旋转而产生的新数独,通过遮盖部分数字达到目的。确实比空棋盘上绘制棋盘再判断解题要简单得多。但是!but!however!先写个最简单的吧,完全是属于那种看了源码解一个格子就能解全部格子的那种。
private void getCeilArray(){ Random rand =new Random(); ArrayList<Integer> list=new ArrayList<Integer>(); for(int i=1;i<=length;i++) list.add(i); for(int i=0;i<length;i++){ int index=rand.nextInt(10)%list.size(); standard[i/3][i%3]=list.get(index); list.remove(index); } }
3*3有了,通过操作最后一行提至第一行,进行两次操作就可以获得完整的3*9
假使我们得到standard为👇
1 2 3
4 5 6
7 8 9
将最后一行提至第一行两次得到两个3*3方阵,拼接后的3*9
_standard_______standard1______standard2_
1 2 3 | 7 8 9 | 4 5 6
4 5 6 | 1 2 3 | 7 8 9
7 8 9 | 4 5 6 | 1 2 3
同样的方法,改变列可以获得9*3,每个方格作为基准时执行两次即可,如下用count作为标记
private int[][] changeCeil(boolean isLine,int count,int[][] origin){ int[][] res=new int[3][3]; if(isLine){ for(int i=0;i<3;i++){ for(int j=0;j<3;j++){ res[(count+i)%3][j]=origin[i][j]; } } }else{ for(int i=0;i<3;i++){ for(int j=0;j<3;j++){ res[i][(count+j)%3]=origin[i][j]; } } } return res; }
如上想是这么想的,但是最后写的时候确实是没用到......就是写死了三个,然后进行数字替换,交换行交换列的操作
交换数字,就是棋盘上所有1变成2,2变成3那样
private List<Integer> buildRandomList() { List<Integer> result = Arrays.asList( 1, 2, 3, 4, 5, 6, 7, 8, 9 ); Collections.shuffle(result); return result; }
public int[][] generateSudokuArray(int[][] sampleArray) { List<Integer> randomList = buildRandomList(); for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { for (int k = 0; k < 9; k++) { if (sampleArray[i][j] == randomList.get(k)) { sampleArray[i][j] = randomList.get((k + 1) % 9); break; } } } } return sampleArray; }
将9*9进行变化就得到最终的了,然后随机挖空就好了。
大佬版 https://github.com/huolizhuminh/AndroidSkills/tree/master/JavaSkillDemo/src/minhui/demo/mysodu/generator
数独的生成结束后!就是页面的问题了!
页面(真的不会写html,我好为难我自己
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" th:fragment="head"> <head> <meta charset="UTF-8"/> <title>Sudoku</title> <script type="text/javascript" th:inline="javascript"> function doCheck(){ /* <![CDATA[ */ let origin = [[${originMap}]]; let tbo = document.getElementById("sudoku-board"); let trs = tbo.getElementsByTagName("tr"); let flag = 0; let markWr = []; for(var i = 0; i < trs.length; i ++){ let target = trs[i].getElementsByTagName("input"); let row = [] for(var j = 0; j < target.length; j ++){ if(target[j].value == ''){ flag = 1; continue; }else if(target[j].value != origin[i][j]){ flag = 2; markWr.push({i,j}) //修改样式 target[j].style.setProperty('background','#FFDAB9') } } } if(flag == 0) alert("bingo"); else if(flag == 1) alert("你都还没填完!"); else if(flag == 2) alert("答案错了~再瞅瞅?") /* ]]> */ } </script> <style> #sudoku-table { margin: 0 auto; max-width: 658px; max-height: 658px; } #sudoku-board { border: 2px solid black; text-align:center; font-size:40px; } #sudoku-board td { padding: 0px; margin: 0px; border-collapse: collapse; border: none; } .mybtn{ text-align:center; margin-left:20%; } input{ width:62px; height:62px; font-size:40px; text-align:center; margin:4px; } </style> </head> <body> <div class="panel-body" id="sudoku-panel"> <table class="table table-striped" id="sudoku-table"> <button class="mybtn" id="check" onclick="doCheck()">检查</button> <tbody id="sudoku-board"> <tr th:each="row:${sudoMap}"> <td th:each="col:${row}"> <input th:if="${col} != 0" th:value="${col}" disabled="disabled"/> <input th:if="${col} == 0" th:type="text" onkeyup="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'')}else{this.value=this.value.slice(0,1)}"/> </td> </tr> </tbody> </table> </div> </body> </html>
2022/9/9 13:36 新增安装程序链接