Java版本的DLX解决数独算法
DLX又称作精确覆盖问题,而今天要说的就是把解数独这个问题转化为一个精确覆盖问题,然后来解决它。
其实这是一个很好的解决问题的思路,很多问题都可以转化为一个精确覆盖问题,然后用接下来这个算法来解决它。
关于DLX的思想,网上有不少的解释,这里我就不再赘述,我会在我的另一篇博客里讲解原理。
首先是整个代码的结构。
总共分为四个类,分别是:
- 主类(StartPoint)
- 转换器类(Transfer)
- 节点类(Node)
- 求解器类(Solver)
这就是我们的代码的主要结构,下面我们将展示代码的细节。
主类(StartPoint)
这是主类,也是整个程序的入口,负责从文件读取数据,调用各种其他类,然后输出结果到文件中,具体如下:
package dLX;import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Vector;public class StartPoint {public static void main(String[] args) {// TODO Auto-generated method stubFile input=new File("D:\\problem.txt");File output = new File("D:\\solution.txt");try {output.createNewFile();FileReader in=new FileReader(input);FileWriter out=new FileWriter(output);char problem[]=new char[(int)input.length()];in.read(problem);if(problem.length<81){out.write("输入不合法");in.close();out.close();return;}Transfer tsf= new Transfer();Vector<Vector<Integer>> matrix = tsf.sudoku2matrix(problem);// 矩阵转换 Solver sudoku=new Solver(matrix,matrix.size(),324);//初始化解题类 if (!sudoku.Search(0)){//调用解题函数 out.write("无解");in.close();out.close();return;}Vector<Integer> solution = tsf.matrix2sudoku(matrix, sudoku.getResult());for (int ix = 0; ix < 81; ++ix){ out.write(solution.get(ix)+""); out.write((ix+1)%9!=0?"" : "\n");}in.close();out.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
节点类(Node)
因为DLX很关键的一个数据结构是链表,所以就需要节点,这里我们就写了一个节点类:
package dLX;public class Node {Node up, down, left, right, colRoot, rowRoot;int Num;//行对象特有,记录行数int Size;//列对象特有,记录该列元素数public Node(){Size=0;Num=-1;}public Node(int i){Size=0;Num=i;}
}
转换器类
因为我们人看到的数独矩阵并不是我们解题需要的01矩阵,所有我们需要一个转换的类,这个类负责把输入的数独矩阵转换成01矩阵便于解题,当解题结束时,再负责把结果转换成便于人类阅读的数独矩阵,这就是这个类的作用:
package dLX;import java.util.Vector;public class Transfer {Vector<Vector<Integer>> sudoku2matrix(char[] problem){Vector<Vector<Integer>> matrix=new Vector<Vector<Integer>>();for (int ix = 0; ix < 81; ++ix){ int val = problem[ix] - '0';Vector<Integer> current_row=new Vector<Integer>();for(int i=0;i<324;i++){current_row.add(new Integer(0));}if (val != 0){current_row.set(ix, new Integer(1));current_row.set(81 + ix/9*9 + val -1, new Integer(1));current_row.set(162 + ix%9*9 +val -1, new Integer(1));current_row.set(243 + (ix/9/3*3+ix%9/3)*9 +val -1, new Integer(1));matrix.add(current_row);continue;}for (int jx = 0; jx < 9; ++jx){Vector<Integer> current_row2=new Vector<Integer>();for(int i=0;i<324;i++){current_row2.add(new Integer(0));}current_row2.set(ix, new Integer(1));current_row2.set(81 + ix/9*9 + jx, new Integer(1));current_row2.set(162 + ix%9*9 +jx , new Integer(1));current_row2.set(243 + (ix/9/3*3+ix%9/3)*9 +jx , new Integer(1));matrix.add(current_row2);}}return matrix;}Vector<Integer> matrix2sudoku(Vector<Vector<Integer>> matrix, Vector<Integer> result){Vector<Integer> solution=new Vector<Integer>();for(int i=0;i<81;i++){solution.add(new Integer(0));}for (int ix = 0; ix < 81; ++ix){Vector<Integer> current = matrix.get(result.get(ix)-1);int pos = 0, val = 0;for (int jx = 0; jx < 81; ++jx){if (current.get(jx) == 1){break;} ++pos;}for (int kx = 81; kx < 162; ++kx){if (current.get(kx) == 1)break;++val;}solution.set(pos, val%9 + 1);}return solution;}
}
求解器类
这个类就是解决问题的核心了,它负责核心的解决问题,接收转换类转换得到的01矩阵来解决问题,提供结果,具体代码如下:
package dLX;import java.util.Vector;public class Solver {Node Head;Vector<Integer> result;int _row, _col, _updates;public static final int INT_MAX=2147483647;public Solver(Vector<Vector<Integer>> matrix, int m, int n){result=new Vector<Integer>();Head = new Node();Head.up = Head;Head.down = Head;Head.right = Head;Head.left = Head;_row=m;_col=n;_updates=0;init();link(matrix);}void init(){Node newNode;//表头位置向后插入,构造列对象for (int ix = 0; ix < _col; ++ix){newNode = new Node();newNode.up = newNode;newNode.down = newNode;newNode.right = Head.right;newNode.left = Head;newNode.right.left = newNode;Head.right = newNode;}//表头位置向下插入,构造行对象for (int ix = 0; ix < _row; ++ix){newNode = new Node(_row-ix);//注意序号是_row-ixnewNode.down = Head.down;newNode.up = Head;newNode.down.up = newNode;Head.down = newNode;}}void link(Vector<Vector<Integer>> matrix){Node current_row, current_col, newNode, current;//当前行对象,当前列对象,新节点,当前节点current_row = Head;for (int row = 0; row < _row; ++row){current_row = current_row.down;current_col = Head;for (int col = 0; col < _col; ++col){current_col = current_col.right;if (matrix.get(row).get(col) == 0)//矩阵上为0的位置不设置节点continue;newNode = new Node();newNode.colRoot = current_col;newNode.rowRoot = current_row;//设置当前节点对应的行列对象newNode.down = current_col;newNode.up = current_col.up;newNode.up.down = newNode;current_col.up = newNode;//链接当前节点到列双向链尾端if (current_row.Size == 0){//行双向链不应该把行对象包含进来current_row.right = newNode;newNode.left = newNode;newNode.right = newNode;current_row.Size++;}current = current_row.right;//设置当前节点(即行对象右的节点)newNode.left = current.left;newNode.right = current;newNode.left.right = newNode;current.left = newNode;//链接当前节点到行双向链尾端current_col.Size++;}}}void cover(Node cRoot){//覆盖列++_updates;cRoot.left.right = cRoot.right;cRoot.right.left = cRoot.left;//删除该列对象Node i, j;i = cRoot.down;while (i != cRoot){j = i.right; while (j != i){j.down.up = j.up;j.up.down = j.down;j.colRoot.Size--;j = j.right;}i = i.down;}}void recover(Node cRoot){Node i, j;i = cRoot.up;while (i != cRoot){j = i.left;while (j != i){j.colRoot.Size++;j.down.up = j;j.up.down = j;j = j.left;}i = i.up;}cRoot.right.left = cRoot;cRoot.left.right = cRoot;}boolean Search(int k){if (Head.right == Head)//列空,则成功找到一组行的集合return true;Node cRoot, c;cRoot=new Node();int minSize = INT_MAX;for(c = Head.right; c != Head; c = c.right){//选择情况最少的列进行尝试 if (c.Size < minSize){minSize = c.Size;cRoot = c;if (minSize == 1){break;}if (minSize == 0){//有一列为空,失败return false;}}}cover(cRoot);//覆盖这一列 Node current_row, current;for (current_row = cRoot.down; current_row != cRoot; current_row = current_row.down){result.add(current_row.rowRoot.Num);//将该行加入result中for (current = current_row.right; current != current_row; current = current.right){cover(current.colRoot);//delete other c}if (Search(k+1)){return true;}for (current = current_row.left; current != current_row; current = current.left)recover(current.colRoot);//还原 result.remove(result.size()-1);//发现该行不符合要求,还原result}recover(cRoot);return false;}Vector<Integer> getResult(){ return result;}int getUpdates(){ return _updates;}}
Java版本的DLX解决数独算法相关推荐
- IDEA java版本降级编译,解决JDK版本导致Unsupported major.minor version 52.0 error
Intellij IDEA使用教程相关系列 目录 具体的操作,这位博友整理得很详细https://blog.csdn.net/huyishero/article/details/61916516 补充 ...
- java回溯算法解决数独_js回溯算法解决数独问题
直接上代码 代码里面注释很清晰 传说中的最难数组大概是在20ms左右解决 /** * 数独算法 */ class Sudoku { constructor({ display = false, sud ...
- POJ2676,HDU4069解决数独的两种实现:DFS、DLX
搜索实现:解决数独有两种思考策略,一种是枚举当前格能填的数字的种数,这里有一优化策略就是先搜索能填入种数小的格子:另一种是考虑处理某一行(列.宫)时,对于某一个没用过的数字,若该行(列.宫)只有一个可 ...
- Leetcode算法Java全解答--37. 解数独
Leetcode算法Java全解答–37. 解数独 文章目录 Leetcode算法Java全解答--37. 解数独 题目 想法 结果 总结 代码 我的答案 大佬们的答案 测试用例 其他 题目 编写一个 ...
- JAVA算法:走迷宫回溯算法设计(JAVA版本)
JAVA算法:走迷宫回溯算法设计(JAVA版本) 迷宫数组 int[][] maze = { {0, 1, 0, 0, 0}, {0, ...
- 贪心算法(Java版本)
一.贪心算法 1.算法描述 贪心算法(Greedy algorithm),又叫做贪婪算法. 在对问题求解时,不从整体考虑,而是从问题的某一个初始解出发,每一步选择中都采取在当前状态下最好或最优的选择( ...
- 我爱Java系列---【 maven依赖版本冲突的解决方法】
我爱Java系列---[ maven依赖版本冲突的解决方法] 参考文章: (1)我爱Java系列---[ maven依赖版本冲突的解决方法] (2)https://www.cnblogs.com/hu ...
- 关于eclpse java项目与tomcat jdk版本不一致的解决方法
最近,在eclipse中tomcat(jdk1.7)添加项目的时候,项目添加不进去,报jdk(项目中jdk1.8)版本不一致的错误.下面是我的解决过程: 选中项目按ALT+回车 一.选择替换jdk如下 ...
- 插入排序算法 java_排序算法实现-插入排序(Java版本)
原标题:排序算法实现-插入排序(Java版本) 插入排序(英语:Insertion Sort)是一种简单直观的排序算法.它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到 ...
最新文章
- 482. License Key Formatting
- 【Linux迁移到Windows服务器时的注意事项】
- 开发日记-20190624 关键词 读书笔记《Linux 系统管理技术手册(第二版)》DAY 1
- .NET微服务架构及API网关
- stm32CAN波特率计算小程序(QT源码)
- USB接口直接焊线的顺序记录
- Web组件 – 构建商业化应用的基石
- Java中常用的测试工具JUnit
- 用PL/SQL Develpoer工具完成导入和导出
- 第一行代码(第二版)全书代码下载
- SQL基础实例(学生课程系统)
- vue 如何处理两个组件异步问题_Vue异步组件处理路由组件加载状态的解决方案...
- P1491 集合位置
- 实时取色器(RGB)
- LaTex笔记二:visio画图并保存为eps格式
- matlab小波变换,图像处理
- oracle 自动提交 配置文件,使用 netcfg 命令配置配置文件 - Oracle Solaris 管理:网络接口和网络虚拟化...
- Tair存储引擎简单介绍以及常见API操作
- html分列代码,实现分列的两段excel vba 分列代码
- 微计算机应用是核心吗,北大核心哪个杂志好投