JAVA暴力求解数独

数独这种游戏想必大家都玩过吧,对于一个9*9的方阵,每行每列都包含1~9共九个数字,一共有九个九宫格,每个九宫格也同样包含1~9共九个数字,根据已经填充的数字完成整个9*9的方阵。

基本思想:暴力破解,采用回溯法,一个一个数字尝试,利用一个堆栈保存数独的每个状态,对于这个堆栈我们可以定义为一个一维整型数组,数组中的数字代表已经填充过的第i行,第j列,大小为k的数字,可以使用一个三位数来保存这些信息,形式如下:

                                                              stack[top++] = i*100+j*10+k;

对于数独,我们可以使用一个二位数组表示,其中0表示为该处没有元素,具体代码如下所示:

import java.util.Scanner;

public class Sudoku {
	int sudoku[][];
	int blank;									/*blank表示初始状况下的数独中空白元素的个数*/
	/*构造函数初始化sudoku数组和blank*/
	public Sudoku() {
		sudoku = new int[9][9];
		for(int i=0;i<9;i++) {
			for(int j=0;j<9;j++) {
				sudoku[i][j] = 0;
			}
			blank = 81;
		}
	}
	/*按行读入并初始化数独*/
	public void init() {
		Scanner in = new Scanner(System.in);
		for(int i=0;i<9;i++) {
			for(int j=0;j<9;j++) {
				sudoku[i][j] = in.nextInt();
				if(sudoku[i][j]!=0) {
					blank--;
				}
			}
		}
	}
	/*利用已有数组初始化数独*/
	public void init(int data[][]) {
		for(int i=0;i<9;i++) {
			for(int j=0;j<9;j++) {
				sudoku[i][j] = data[i][j];
				if(sudoku[i][j]!=0) {
					blank--;
				}
			}
		}
		System.out.println(blank);
	}
	/*输出数独*/
	public void printSudoku() {
		for(int i=0;i<9;i++) {
			for(int j=0;j<9;j++) {
				System.out.print(sudoku[i][j]+" ");
			}
			System.out.println();
		}
	}
	/*判断数字num是否存在于第row行*/
	public boolean isInRow(int num,int row) {
		for(int i=0;i<9;i++) {
			if(sudoku[row][i]==num) {
				return true;
			}
		}
		return false;
	}
	
	/*判断数字num是否存在于第col列*/
	public boolean isInCol(int num,int col) {
		for(int i=0;i<9;i++) {
			if(sudoku[i][col]==num) {
				return true;
			}
		}
		return false;
	}
	
	/*判断数字num是否存在于第row行,第col列所在的九宫格*/
	public boolean isInCell(int num,int row,int col) {
		int startRow = row/3*3;
		int startCol = col/3*3;
		int endRow = startRow+3;
		int endCol = startCol+3;
		for(int i=startRow;i<endRow;i++) {
			for(int j=startCol;j<endCol;j++) {
				if(sudoku[i][j]==num) {
					return true;
				}
			}
		}
		return false;
	}
	/*求解函数*/
	public boolean solve() {
		int num = 0;
		int data = 1;
		int stack[] = new int[81];				//栈保存一个三位数,第一位代表行标,第二位代表列标,第三位代表该位置的值
		int top = 0;
		int i,j,k;
		for(i=0;i<9;i++) {
			for(j=0;j<9;j++) {
				if(sudoku[i][j]==0) {			//若该处元素为空则可以尝试填入1~9
					for(k=data;k<=9;k++) {
						if(!isInCell(k, i, j)&&!isInCol(k, j)&&!isInRow(k, i)) {
							sudoku[i][j]=k;
							stack[top++] = i*100+j*10+k;						//填入元素后压栈,保存所填元素的基本信息
							if(top==blank) {
								return true;
							}
							data = 1;
							break;
						}
					}
					if(k==10) {
						num = stack[--top];				//弹出上一个填入数独的元素信息,并重新求解
						i = num/100;					
						j = num%100/10-1;				//减一是因为在第二层for循环结束时i会自动加1
						sudoku[i][j+1] = 0;				
						data = num%10+1;				//从下一个数字开始重新尝试
					}
				}
			}
		}
		return false;
	}
	public static void main(String[] args) {
		int data[][] = {
				{0,0,5,3,0,0,0,0,0},
				{8,0,0,0,0,0,0,2,0},
				{0,7,0,0,1,0,5,0,0},
				{4,0,0,0,0,5,3,0,0},
				{0,1,0,0,7,0,0,0,6},
				{0,0,3,2,0,0,0,8,0},
				{0,6,0,5,0,0,0,0,9},
				{0,0,4,0,0,0,0,3,0},
				{0,0,0,0,0,9,7,0,0}
		};
		Sudoku sudoku = new Sudoku();
		sudoku.init(data);
		boolean isfinished = sudoku.solve();
		if(isfinished) {
			sudoku.printSudoku();
		}else {
			System.out.println("ERROR!");
		}
	}

}

讨论:对于数独求解还有更为快速的方法,但同样的代码量会相对多一些,同时对于此程序还有优化的方法,比如说在初始化后进行前处理,把自由度为1的单元先进行求解,然后简化数独中的未知单元的个数。

我在示例中所使用的数独如下:https://baike.baidu.com/item/%E4%B8%96%E7%95%8C%E6%9C%80%E9%9A%BE%E6%95%B0%E7%8B%AC/13848819?fr=aladdin

有兴趣的朋友可以尝试着做一做!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值