DLX又称作精确覆盖问题,而今天要说的就是把解数独这个问题转化为一个精确覆盖问题,然后来解决它。
其实这是一个很好的解决问题的思路,很多问题都可以转化为一个精确覆盖问题,然后用接下来这个算法来解决它。
关于DLX的思想,网上有不少的解释,这里我就不再赘述,我会在我的另一篇博客里讲解原理。

首先是整个代码的结构。
总共分为四个类,分别是:

  1. 主类(StartPoint)
  2. 转换器类(Transfer)
  3. 节点类(Node)
  4. 求解器类(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解决数独算法相关推荐

  1. IDEA java版本降级编译,解决JDK版本导致Unsupported major.minor version 52.0 error

    Intellij IDEA使用教程相关系列 目录 具体的操作,这位博友整理得很详细https://blog.csdn.net/huyishero/article/details/61916516 补充 ...

  2. java回溯算法解决数独_js回溯算法解决数独问题

    直接上代码 代码里面注释很清晰 传说中的最难数组大概是在20ms左右解决 /** * 数独算法 */ class Sudoku { constructor({ display = false, sud ...

  3. POJ2676,HDU4069解决数独的两种实现:DFS、DLX

    搜索实现:解决数独有两种思考策略,一种是枚举当前格能填的数字的种数,这里有一优化策略就是先搜索能填入种数小的格子:另一种是考虑处理某一行(列.宫)时,对于某一个没用过的数字,若该行(列.宫)只有一个可 ...

  4. Leetcode算法Java全解答--37. 解数独

    Leetcode算法Java全解答–37. 解数独 文章目录 Leetcode算法Java全解答--37. 解数独 题目 想法 结果 总结 代码 我的答案 大佬们的答案 测试用例 其他 题目 编写一个 ...

  5. JAVA算法:走迷宫回溯算法设计(JAVA版本)

    JAVA算法:走迷宫回溯算法设计(JAVA版本) 迷宫数组 int[][] maze = {                 {0, 1, 0, 0, 0},                 {0, ...

  6. 贪心算法(Java版本)

    一.贪心算法 1.算法描述 贪心算法(Greedy algorithm),又叫做贪婪算法. 在对问题求解时,不从整体考虑,而是从问题的某一个初始解出发,每一步选择中都采取在当前状态下最好或最优的选择( ...

  7. 我爱Java系列---【 maven依赖版本冲突的解决方法】

    我爱Java系列---[ maven依赖版本冲突的解决方法] 参考文章: (1)我爱Java系列---[ maven依赖版本冲突的解决方法] (2)https://www.cnblogs.com/hu ...

  8. 关于eclpse java项目与tomcat jdk版本不一致的解决方法

    最近,在eclipse中tomcat(jdk1.7)添加项目的时候,项目添加不进去,报jdk(项目中jdk1.8)版本不一致的错误.下面是我的解决过程: 选中项目按ALT+回车 一.选择替换jdk如下 ...

  9. 插入排序算法 java_排序算法实现-插入排序(Java版本)

    原标题:排序算法实现-插入排序(Java版本) 插入排序(英语:Insertion Sort)是一种简单直观的排序算法.它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到 ...

最新文章

  1. 482. License Key Formatting
  2. 【Linux迁移到Windows服务器时的注意事项】
  3. 开发日记-20190624 关键词 读书笔记《Linux 系统管理技术手册(第二版)》DAY 1
  4. .NET微服务架构及API网关
  5. stm32CAN波特率计算小程序(QT源码)
  6. USB接口直接焊线的顺序记录
  7. Web组件 – 构建商业化应用的基石
  8. Java中常用的测试工具JUnit
  9. 用PL/SQL Develpoer工具完成导入和导出
  10. 第一行代码(第二版)全书代码下载
  11. SQL基础实例(学生课程系统)
  12. vue 如何处理两个组件异步问题_Vue异步组件处理路由组件加载状态的解决方案...
  13. P1491 集合位置
  14. 实时取色器(RGB)
  15. LaTex笔记二:visio画图并保存为eps格式
  16. matlab小波变换,图像处理
  17. oracle 自动提交 配置文件,使用 netcfg 命令配置配置文件 - Oracle Solaris 管理:网络接口和网络虚拟化...
  18. Tair存储引擎简单介绍以及常见API操作
  19. html分列代码,实现分列的两段excel vba 分列代码
  20. 微计算机应用是核心吗,北大核心哪个杂志好投

热门文章

  1. selenium操作日历控件
  2. native(百度百科)
  3. SAP EPIC 银企直连 余额查询(建设银行)
  4. 银河计划让闲置带宽共享变宝
  5. 学习笔记之Android四大核心组件详解
  6. 图片 (免费的可商业使用的图片素材)
  7. 机器视觉 Histogram of oriented gradients
  8. Android 升级 gradle 遇到的问题
  9. 爱莉安娜-格兰德推出全新个人香水R.E.M.
  10. python爬虫:百度图片爬虫代码