蓝桥杯——数独JAVA
这几天开始在准备蓝桥杯了,想借此来学习一下算法,提高自己编程的能力,前几天看的一个数独题,虽然知道是用深度优先去做,但是在做的时候还是遇到了些问题,但是感觉回溯算法做题时是有一点套路的,所以决定写下来。
题目:
你一定听说过“数独”游戏。如【图1.png】,玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个同色九宫内的数字均含1-9,不重复。
数独的答案都是唯一的,所以,多个解也称为无解。
本图的数字据说是芬兰数学家花了3个月的时间设计出来的较难的题目。但对会使用计算机编程的你来说,恐怕易如反掌了。
本题的要求就是输入数独题目,程序输出数独的唯一解。我们保证所有已知数据的格式都是合法的,并且题目有唯一的解。
格式要求,输入9行,每行9个字符,0代表未知,其它数字为已知。
输出9行,每行9个数字表示数独的解。
例如:
输入(即图中题目):
005300000
800000020
070010500
400005300
010070006
003200080
060500009
004000030
000009700
程序应该输出:
145327698
839654127
672918543
496185372
218473956
753296481
367542819
984761235
521839764
再例如,输入:
800000000
003600000
070090200
050007000
000045700
000100030
001000068
008500010
090000400
程序应该输出:
812753649
943682175
675491283
154237896
369845721
287169534
521974368
438526917
796318452
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。
思路:
首先这题一看就是回溯法,来分析一下这个算法的实现,首先定义一个全局的二维数组,数组长度为9即可,然后在主函数中输入二维数组数据。然后就开始调用这里面最重要的函数get(int row,int col)row:行下标,col:列下标。
但凡是数组用到了回溯,基本这个函数里的参数都是下标(题目做的还不是很多,只能说目前是这样),然后函数里当超过数组下标值立即将数组输出或者执行其他操作,然后进行判断,这题里当数组值为0时,说明这个位置没有数填进去,那么进行1–10的遍历,开始放值,放值之前要判断这个数是否和所在行,列,以及同色九宫内的数重复(这里用is_row_col(int row,int col,int target)函数和is_nine(int row,int col,int target)函数进行判断),如果返回值都是true,那么进行赋值操作,并且向下一个数组元素递归,当遇到重复值,将数组元素值置0,回溯。
代码
import java.util.Scanner;
public class Main {
private static int[][] data = new int[9][9];
public static void print() {
int t=0;
for(int i=0;i<9;i++) {
for(int j=0;j<9;j++) {
System.out.print(data[i][j]);
t++;
if(t==9) {
System.out.println();
t=0;
}
}
}
}
public static boolean is_row_col(int row,int col,int target) {
boolean test=true;
for(int i=0;i<9;i++) {
if(data[row][i]==target) {
test=false;
break;
}
if(data[i][col]==target) {
test=false;
break;
}
}
return test;
}
public static boolean is_nine(int row,int col,int target) {
boolean test=true;
int row_min=0;
int row_max=0;
int col_min=0;
int col_max=0;
if(row>=0&&row<=2) {
row_min=0;
row_max=2;
}
if(row>=3&&row<=5) {
row_min=3;
row_max=5;
}
if(row>=6&&row<=8) {
row_min=6;
row_max=8;
}
if(col>=0&&col<=2) {
col_min=0;
col_max=2;
}
if(col>=3&&col<=5) {
col_min=3;
col_max=5;
}
if(col>=6&&col<=8) {
col_min=6;
col_max=8;
}
for(int i=row_min;i<=row_max;i++) {
for(int j=col_min;j<=col_max;j++) {
if(data[i][j]==target) {
test=false;
break;
}
}
}
return test;
}
public static void get(int row,int col) {
if(row>=9) {
print();
System.exit(0);
}
if(data[row][col]==0) {
for(int t=1;t<10;t++) {
if(is_row_col(row,col,t)&&is_nine(row,col,t)){
data[row][col]=t;
get(row+(col+1)/9,(col+1)%9);//继续向下递归
}
data[row][col]=0;//回溯
}
}else {
get(row+(col+1)/9,(col+1)%9);
}
}
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
for(int i=0;i<9;i++) {
String ss = s.nextLine();
for(int j=0;j<9;j++) {
data[i][j]=Integer.parseInt(ss.charAt(j)+"");
}
}
get(0,0);
}
}
欢迎指出问题!
回溯法其实有一个基本的公式,在进行递归的函数中,其实有一定的规律:
public void get(int index){
if(index==数组长度){
//需要进行的操作
}
if(相关条件判断){
//进行操作
get(index+1);//向下递归
//递归失败,将上面的操作取消
}
```