日行一算(数独游戏)

题目#

主要考察:对深度优先搜索和回溯的理解和基本功

题目描述
大家对数独游戏一定都不陌生:在一个99的方阵上划分出9个更小的33的子方阵。每个格子上填1到9这9个数字中的一个。有些格子上的数字确定了,不能再改动。要求把剩余尚未确定的地方填上适当的数字并且保证:
1.任意一行的9个数字均不相同
2.任意一列的9个数字均不相同
3.任意子方阵中的9个数字均不相同。

解答要求
时间限制:1000ms, 内存限制:100MB
输入
每个输入文件一共9行,每行9个数字(0到9),分别表示该行9个位置上填的数。0表示该位置上的数字尚未确定,需要由您来确定,1到9表示该位置上的数字已经确定,不能再改动。(所提供的输入保证有且仅有一种解法)

输出
共9行,输出数独的填数方法。

样例
输入样例

Copy
103000509 002109400 000704000 300502006 060000050 700803004 000401000 009205800 804000107

输出样例

Copy
143628579 572139468 986754231 391542786 468917352 725863914 237481695 619275843 854396127

解题思路#

一开始直接使用暴力回溯,会超时到两秒。代码如下所示。

Copy
import java.util.Scanner; public class Main { static int matrix[][] = new int[11][11]; public static void main(String[] args) { Scanner sc = new Scanner(System.in); for (int i = 1; i <= 9; ++i) { String input[] = sc.nextLine().split(""); for (int j = 1; j <= 9; ++j) { matrix[i][j] = Integer.valueOf(input[j - 1]); } } backTrace(1, 1); } // 回溯 private static void backTrace(int i, int j) { if (i == 9 && j == 10) { // 已经成功了,打印数组即可 printArray(); return; } else if (j == 10) { // 已经到了列末尾了,还没到行尾,就换行 i++; j = 1; } // 如果i行j列是空格,那么才进入给空格填值的逻辑 if (matrix[i][j] == 0) { for (int k = 1; k <= 9; k++) { // 判断给i行j列放1-9中的任意一个数是否能满足规则 if (check(i, j, k)) { // 将该值赋给该空格,然后进入下一个空格 matrix[i][j] = k; backTrace(i, j + 1); // 初始化该空格 matrix[i][j] = 0; } } } else { // 如果该位置已经有值了,就进入下一个空格进行计算 backTrace(i, j + 1); } } // 检查 private static boolean check(int row, int col, int number) { // 判断该行该列是否有重复数字 for (int i = 1; i <= 9; i++) { if (matrix[row][i] == number || matrix[i][col] == number) { return false; } } // 判断小九宫格是否有重复 int tempRow = (row - 1) / 3; int tempLine = (col - 1) / 3; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (matrix[tempRow * 3 + i + 1][tempLine * 3 + j + 1] == number) { return false; } } } return true; } /** * 打印矩阵 */ public static void printArray() { for (int i = 1; i <= 9; i++) { for (int j = 1; j <= 9; j++) { System.out.print(matrix[i][j]); } System.out.println(); } System.out.println(); } }

因此需要将代码合理剪枝,在找到目的数独结果之后,停止搜索。

Copy
static boolean flag = false; // 回溯 private static void backTrace(int i, int j) { if (i == 9 && j == 10) { // 已经成功了,打印数组即可 printArray(); flag = true; return; } else if (j == 10) { // 已经到了列末尾了,还没到行尾,就换行 i++; j = 1; } // 剪枝,避免找到之后继续迭代 if (flag) { return; } // 如果i行j列是空格,那么才进入给空格填值的逻辑 if (matrix[i][j] == 0) { for (int k = 1; k <= 9; k++) { // 判断给i行j列放1-9中的任意一个数是否能满足规则 if (check(i, j, k)) { // 将该值赋给该空格,然后进入下一个空格 matrix[i][j] = k; backTrace(i, j + 1); // 初始化该空格 matrix[i][j] = 0; } } } else { // 如果该位置已经有值了,就进入下一个空格进行计算 backTrace(i, j + 1); } }

该种情况下,依然会有超时的情况发生,不过时间已经从两秒降到了1.2S,此时距离目标1S已经很接近了。由于我们知道在不使用其他算法,仅仅回溯算法的情况下,如果想要进一步压缩时间的话,就只能在探测是否重复的地方进行优化。因为该步骤会导致我们再迭代18次进行检查。

如果在这里使用空间换取时间进行优化的话,我们就能将O(18N3)的算法压缩成O(N3),此时应该能够将时间进行进一步压缩。

最终的AC代码如下,运行时间0.8s:

Copy
import java.util.Scanner; public class Main { static int matrix[][] = new int[11][11]; static boolean flag = false; static boolean col[][] = new boolean[11][11]; static boolean row[][] = new boolean[11][11]; static boolean zheng[][] = new boolean[11][11]; public static void main(String[] args) { Scanner sc = new Scanner(System.in); for (int i = 1; i <= 9; ++i) { String input[] = sc.nextLine().split(""); for (int j = 1; j <= 9; ++j) { matrix[i][j] = Integer.valueOf(input[j - 1]); row[i][matrix[i][j]] = true; col[j][matrix[i][j]] = true; zheng[(i - 1) / 3 * 3 + (j - 1) / 3 + 1][matrix[i][j]] = true; } } backTrace(1, 1); } // 回溯 private static void backTrace(int i, int j) { if (i == 9 && j == 10) { // 已经成功了,打印数组即可 printArray(); flag = true; return; } else if (j == 10) { // 已经到了列末尾了,还没到行尾,就换行 i++; j = 1; } // 剪枝,避免找到之后继续迭代 if (flag) { return; } int zhengIndex = (i - 1) / 3 * 3 + (j - 1) / 3 + 1; // 如果i行j列是空格,那么才进入给空格填值的逻辑 if (matrix[i][j] == 0) { for (int k = 1; k <= 9; k++) { // 判断给i行j列放1-9中的任意一个数是否能满足规则 if ((!row[i][k]) && (!col[j][k]) && (!zheng[zhengIndex][k])) { // 将该值赋给该空格,然后进入下一个空格 matrix[i][j] = k; row[i][k] = true; col[j][k] = true; zheng[zhengIndex][k] = true; backTrace(i, j + 1); row[i][k] = false; col[j][k] = false; zheng[zhengIndex][k] = false; // 初始化该空格 matrix[i][j] = 0; } } } else { // 如果该位置已经有值了,就进入下一个空格进行计算 backTrace(i, j + 1); } } /** * 打印矩阵 */ public static void printArray() { for (int i = 1; i <= 9; i++) { for (int j = 1; j <= 9; j++) { System.out.print(matrix[i][j]); } System.out.println(); } System.out.println(); } }
posted @   dawa大娃bigbaby  阅读(213)  评论(5编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示
CONTENTS