数独问题有一个很重要的概念是“相关20格”,就是指一个单元格它的同行、同列以及同一个小九宫格的共计20个小单元格
。每个单元格的变化只会直接影响它的相关20格。
首先为每个单元格建立一个候选列表,并且初始化的时候用已经给出的提示数对每个单元格的候选列表进行排除。然后循环对每个单元格进行试数,每进行一次试数,就需要更新这个单元格的相关20格的候选列表,如果这个单元格的相关20格中某个单元格的候选列表只有一个数,那么把这个单元格设置成确定状态,同时再对这个单元格的相关20格的候选列表进行排除(这是一个递归的过程)。重复这个过程,直至所有的单元格都已经是固定状态则结束程序。

注意:循环和递归的过程中最好每次更改状态的时候都新建一个状态,也就是使用Immutable模式,这样可以显著降低程序的复杂性,降低出错的可能。

import java.util.*;public class Sudoku {//数独的行数和列数private static final int ROW_LIMIT = 9;private static final int COL_LIMIT = 9;private static final int TOTAL_NUM = ROW_LIMIT * COL_LIMIT;private static final int PLACEHOLDER = 0;//小九宫格的大小和数目private static final int ROW_BLK_LIMIT = 3;   //每行三个小九宫格private static final int COL_BLK_LIMIT = 3;   //每行三个小九宫格private static final int ROW_BLK_NUM = 3;     //每行小九宫格包含三个小格子private static final int COL_BLK_NUM = 3;     //每列小包含三个小格子static class State {final Cell[][] cells = new Cell[ROW_LIMIT][COL_LIMIT];int fixedNum = 0;   //已经确定下来的小方格数public State() {}//(row, col)是将要被固定的小方格的位置,num是它将要被填入的数字public State(Cell[][] cellsNeedCopy, int fixedNum, int row, int col, int num) {this.fixedNum = fixedNum;for (int i = 0; i < ROW_LIMIT; i++) {for (int j = 0; j < COL_LIMIT; j++) {if (i == row && j == col) {cells[i][j] = new Cell(num, true, null);continue;}cells[i][j] = new Cell(cellsNeedCopy[i][j]);}}}public void init(int[][] data) {//计算每一行中出现的数字Map<Integer, Set<Integer>> rowSet = new HashMap<>();//计算每一列中出现的数字Map<Integer, Set<Integer>> colSet = new HashMap<>();//计算第一个小九宫格中出现的数字Map<Integer, Set<Integer>> cellSet = new HashMap<>();for (int i = 0; i < 9; i++) {rowSet.put(i, new HashSet<>());colSet.put(i, new HashSet<>());cellSet.put(i, new HashSet<>());}//先初始化State的Cell矩阵for (int i = 0; i < ROW_LIMIT; i++) {for (int j = 0; j < COL_LIMIT; j++) {cells[i][j] = new Cell();cells[i][j].num = data[i][j];if (data[i][j] != 0) {rowSet.get(i).add(data[i][j]);colSet.get(j).add(data[i][j]);cellSet.get((i / COL_BLK_NUM) * ROW_BLK_LIMIT + j / COL_BLK_NUM).add(data[i][j]);fixedNum++;cells[i][j].fixed = true;cells[i][j].candidates = null;} else {cells[i][j].fixed = false;cells[i][j].candidates = new HashSet<>();for (int k = 1; k <= 9; k++) {cells[i][j].candidates.add(k);}}}}//尽可能地减少候选列表中的数字for (int i = 0; i < ROW_LIMIT; i++) {for (int j = 0; j < COL_LIMIT; j++) {if (cells[i][j].fixed) continue;cells[i][j].candidates.removeAll(rowSet.get(i));cells[i][j].candidates.removeAll(colSet.get(j));cells[i][j].candidates.removeAll(cellSet.get((i / COL_BLK_NUM) * ROW_BLK_LIMIT + j / COL_BLK_NUM));}}}}static class Cell {int num;boolean fixed;Set<Integer> candidates = new HashSet<>();public Cell(int num, boolean fixed, Set<Integer> candidates) {this.num = num;this.fixed = fixed;this.candidates = new HashSet<>();if (candidates != null) {for (int i : candidates) {this.candidates.add(i);}}}public Cell() {}public Cell(final Cell c) {num = c.num;fixed = c.fixed;candidates = new HashSet<>();if (c.candidates != null) {for (int i : c.candidates) {this.candidates.add(i);}}}}public void printState(final State state) {for (int i = 0; i < ROW_LIMIT; i++) {for (int j = 0; j < COL_LIMIT; j++) {System.out.print(state.cells[i][j].num + " ");}System.out.println();}}//寻找第(row, col)个小方格的答案public void findSolution(final State state, int row, int col) {
//        System.out.println("Now (" + row + ", " + col + ")");if (state.fixedNum == TOTAL_NUM) {System.out.println("Find solution in findSolution!");printState(state);return;}if (row >= ROW_LIMIT || col >= COL_LIMIT) return;Cell current = state.cells[row][col];if (current.fixed) {int index = row * ROW_LIMIT + col + 1;  //用于获得下一个小方格的行和列findSolution(state, index / ROW_LIMIT, index % ROW_LIMIT);} else {//对于这个小方格,遍历尝试其所有的候选列表for (int num : current.candidates) {State newState = setCellToFixed(state, row, col, num);if (newState != null) { //如果把index设置成num是合理的,并且已经从其相关20格的候选列表中去除了numif (newState.fixedNum == TOTAL_NUM) {return;}int index = row * ROW_LIMIT + col + 1;  //用于获得下一个小方格的行和列findSolution(newState, index / ROW_LIMIT, index % ROW_LIMIT);}}}}private State setCellToFixed(final State state, int row, int col, int num) {final State newState = new State(state.cells, state.fixedNum + 1, row, col, num);if (newState.fixedNum == TOTAL_NUM) {System.out.println("Find solution!");printState(newState);return newState;}//检查其相关20格是否合理if (!isExclusive(newState, row, col, num)) return null;//检查其相关20格是否有某个小方格的候选列表是唯一的,如果有,直接设置成那个数final State newnewState = processSingleCandidate(newState, row, col);if (newnewState == null) return null;return newnewState;}//此函数可以在原有的State上面操作private State processSingleCandidate(State state, int row, int col) {Cell cell;//遍历行for (int c = 0; c < ROW_LIMIT; c++) {cell = state.cells[row][c];if (!cell.fixed && cell.candidates.size() == 1) {int num = cell.candidates.iterator().next();State newState = setCellToFixed(state, row, c, num);if (newState == null) return null;state = newState;if (state.fixedNum == TOTAL_NUM) {return state;}}}//遍历列for (int r = 0; r < COL_LIMIT; r++) {cell = state.cells[r][col];if (!cell.fixed && cell.candidates.size() == 1) {int num = cell.candidates.iterator().next();State newState = setCellToFixed(state, r, col, num);if (newState == null) return null;state = newState;if (state.fixedNum == TOTAL_NUM) {return state;}}}//遍历其所在的小九宫格int baseRow = (row / ROW_BLK_NUM) * ROW_BLK_NUM;int baseCol = (col / COL_BLK_NUM) * COL_BLK_NUM;for (int r = 0; r < ROW_BLK_NUM; r++) {for (int c = 0; c < COL_BLK_NUM; c++) {cell = state.cells[baseRow + r][baseCol + c];if (!cell.fixed && cell.candidates.size() == 1) {int num = cell.candidates.iterator().next();
//                    System.out.println("Find sole solution to cell: (" + r + " ," + c + ") = " + num);State newState = setCellToFixed(state, baseRow + r, baseCol + c, num);if (newState == null) return null;state = newState;if (state.fixedNum == TOTAL_NUM) {return state;}}}}return state;}//检查将第row行第col列的小方格设置成num是否合理(通过检查其相关20格是否有重复的num),并在检查的过程中从其相关20格的候选列表中去除numprivate boolean isExclusive(final State state, int row, int col, int num) {return rowExclusive(state, row, col, num) && colExclusive(state, row, col, num) && blockExclusive(state, row, col, num);}//确定num是否在当前状态下小九宫格内唯一,并从相应小方格的候选列表中去除numprivate boolean blockExclusive(final State state, int row, int col, int num) {int baseRow = (row / ROW_BLK_NUM) * ROW_BLK_NUM;int baseCol = (col / COL_BLK_NUM) * COL_BLK_NUM;Cell cell;for (int r = 0; r < ROW_BLK_NUM; r++) {for (int c = 0; c < COL_BLK_NUM; c++) {if (baseRow + r == row && baseCol + c == col) continue;cell = state.cells[baseRow + r][baseCol + c];if (cell.num == num) return false;if (!cell.fixed) {cell.candidates.remove(num);if (cell.candidates.isEmpty()) return false;}}}return true;}//确定num是否在当前状态下列内唯一,并从相应小方格的候选列表中去除numprivate boolean colExclusive(final State state, int row, int col, int num) {Cell cell;for (int r = 0; r < COL_LIMIT; r++) {if (r == row) continue;cell = state.cells[r][col];if (cell.num == num) return false;if (!cell.fixed) {cell.candidates.remove(num);if (cell.candidates.isEmpty()) return false;}}return true;}//确定num是否在当前状态下行内唯一,并从相应小方格的候选列表中去除numprivate boolean rowExclusive(final State state, int row, int col, int num) {Cell cell;for (int i = 0; i < ROW_LIMIT; i++) {if (i == col) continue;cell = state.cells[row][i];if (cell.num == num) return false;if (!cell.fixed) {cell.candidates.remove(num);if (cell.candidates.isEmpty()) return false;}}return true;}public static void main(String[] args) {
//        int[][] data = {//                {0, 0, 0, 0, 0, 6, 0, 0, 1},
//                {9, 0, 0, 0, 0, 0, 3, 7, 6},
//                {7, 1, 0, 0, 4, 0, 0, 0, 0},
//                {1, 7, 0, 8, 0, 0, 0, 0, 3},
//                {0, 3, 0, 0, 0, 0, 0, 1, 0},
//                {6, 0, 0, 0, 0, 3, 0, 5, 8},
//                {0, 0, 0, 0, 3, 0, 0, 6, 5},
//                {3, 5, 1, 0, 0, 0, 0, 0, 2},
//                {8, 0, 0, 1, 0, 0, 0, 0, 0}
//        };int[][] data = {{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}};State state = new State();state.init(data);new Sudoku().findSolution(state, 0, 0);}
}

数独问题的Java语言求解相关推荐

  1. java兔子问题编程思路详解_java语言求解兔子问题代码分析

    1.思考 兔子问题,是费氏数列的形象化说法,它是由一位名为Fibonacci的数学家在它的著作中提出的一个问题. 2.描述 它体术的问题是:若有一只免子每个月生一只小免子,一个月后小免子也开始生产.起 ...

  2. java语言实现一个长度为n_Java语言实现求解一元n次多项式的方法示例[Java代码]...

    本文主要向大家介绍了Java语言实现求解一元n次多项式的方法示例,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助. 项目需要做趋势预测,采用线性拟合.2阶曲线拟合和指数拟合的算法,各种线 ...

  3. 下面关于java语言说法不正确的是_数据库应用技术复习 求解1下面关于JAVA语言的说法,错误的是(  )。A.JAVA语言是一种面向对象的程序设计语言。B...

    数据库应用技术复习 求解1下面关于JAVA语言的说法,错误的是( ).A.JAVA语言是一种面向对象的程序设计语言.B 数据库应用技术复习 求解 1下面关于JAVA语言的说法,错误的是( ). A.J ...

  4. 枚举求解单词方阵(洛谷P1101题题解,Java语言描述)

    题目要求 P1101题目链接 分析 可以用DFS做,但我立下了个Flag,所以就用了朴素的枚举来做.... 结果,我的天哪,做了好几个小时-- 其实这种地图题,真的适合 DFS or BFS or D ...

  5. 巧用Calendar求解黑色星期五问题(洛谷P1202题题解,Java语言描述)

    题目要求 P1202题目链接 分析 嘿嘿嘿,直接使用Calendar,注意的问题是一月对应的是0,但年份和日期都是真的. Calendar.DAY_OF_WEEKCalendar.DAY\_OF\_W ...

  6. 方程求解(洛谷P1689题题解,Java语言描述)

    题目要求 P1689题目链接 分析 是一个小的模拟题,可以穷举情况并得到答案. 共有六种可能: A+B=XA+B=XA+B=X → X=A+BX=A+BX=A+B A+X=BA+X=BA+X=B → ...

  7. 动态规划求解装箱问题(洛谷P1049题题解,Java语言描述)

    题目要求 P1049题目链接 分析 这种题不能贪心,大家都懂的,应该使用DP. 比如容量为7,有三个物品体积是1,2,5,你要是先装小的,就只能装3,剩下4,但实际上可以装的只剩一个. 如果容量为10 ...

  8. 动态规划求解疯狂的采药问题(洛谷P1616题题解,Java语言描述)

    题目要求 P1616题目链接 分析 参考这篇文章自己做出来的 → Here 我就不讲了. AC代码(Java语言描述) import java.util.Scanner;public class Ma ...

  9. 动态规划求解限时采药问题(洛谷P1048题题解,Java语言描述)

    题目要求 P1048题目链接 分析 荐读:大神博文 -> <聊聊动态规划与记忆化搜索> 这题就是一个标准的DP水题,对于不会DP的萌新,太难了!对于整天搞DP的算法大佬,水爆了! 荐 ...

最新文章

  1. hdu1042 java_N! hdu1042 | 学步园
  2. Python3 文件的重命名
  3. c++socket多个客户端通过不同端口与一个服务端通信_手写RPC,深入底层理解整个RPC通信...
  4. AIRAVATA:量化机器学习中的参数泄露
  5. jboss8日志级别设置_罐中研讨会:设置JBoss BPM Suite全日研讨会
  6. java stream 多个filter_如何在Java Stream上应用多个过滤器?
  7. c++ 实现ping
  8. 随想录(用好红黑树)
  9. 在RHEL4.0下面安装oracle10g数据库
  10. elk日志分析系统_ELK 日志分析系统
  11. mysql 5.7 binlog 压缩_MySQL binlog 压缩功能的相关介绍-爱可生
  12. c语言指针详解pdf下载,C语言指针详解.pdf
  13. 计算机组成原理_在线作业1,电子科大《计算机组成原理》在线作业1
  14. 主分区损坏diskgenius_使用Diskgenius完成磁盘结构损坏且无法读取的的情况
  15. 新浪cn邮箱服务器,新浪CN免费邮箱outlook 设置详解
  16. C盘Windows XP,D盘Windows7,双系统安装纪录
  17. java中hash值什么意思_到底什么是hash?它起什么作用?
  18. python中true什么意思_python中的bool是什么意思
  19. 快递查询工具,如何查看单号在每个时间段的具体信息
  20. ECE与计算机相关吗,ECE(电子和计算机工程)相关专业的申请经验分享

热门文章

  1. Kotlin——幸运大转盘
  2. html 中的 header-作用与使用
  3. AD(十七)PCB板框的评估及层叠设计
  4. 数据库update(动态更新)-SqlServer
  5. UWA Pipeline 功能详解|如何快速创建UWA服务
  6. 买了套新概念英语,感觉不错
  7. wxPython + pyOpenGL,打造三维数据分析的利器
  8. DirectX3D 点精灵绘制失效的问题
  9. python---发送邮件(zmail)
  10. 【读书笔记】壹百度--十年千倍的29条法则