数独C语言编程-正推与回溯

【数独C语言】:

1.行、宫、列进行判断下的正推方法(不用回溯法)可以解决大部分问题;

2.使用回溯法递归方式解决剩下的情况(效率很不错);

#ifndef __SUDOKU_H_
#define __SUDOKU_H__
#include<stdio.h>
int SUDOKUarr[9][9] = {							//临时数组,作为测试题目,或示范 
			{0,4,2,0,6,3,0,0,9},
			{6,0,0,0,1,0,0,0,5},
			{3,0,0,0,2,0,4,8,0},
			{1,0,0,5,0,2,6,0,8},
			{4,0,0,0,0,7,0,0,1},
			{9,0,5,6,0,0,0,0,7},
			{0,3,6,0,5,0,0,0,2},
			{2,0,0,0,7,0,0,0,4},
			{7,0,0,2,9,0,8,5,0},
		};
void SUDOKUinput(int arr[9][9]){					//输入和输出(输出改写为展现宫的形式)
	char ques[9][9];
	int i,j;
	for(i=0;i<9;i++) scanf("%s",ques[i]);	
	for(i=0;i<9;i++){
		for(j=0;j<9;j++) arr[i][j] = (int)ques[i][j] - 48;
	}
}
void SUDOKUputout(int arr[9][9]){
	int i,j,d,len = 25;
	printf("\n");
	for(i=0;i<9;i++){
		if(i%3==0){
			for(d=0;d<len;d++){printf("-");}
			printf("\n");
		}
		for(j=0;j<9;j++){
			if(j%3==0){
				printf("| ");
			}
			printf("%d ",arr[i][j]);
		}
		printf("|\n");
	}
	for(d=0;d<len;d++){printf("-");}
}
									//对行,宫,列进行判断,是否可以填入num 
int SUDOKUright(int num,int x,int y){
	int i,j,glie = x/3,grow = y/3;
	if(SUDOKUarr[x][y]!=0) return 0;
	for(i=3*glie;i<glie*3+3;i++){
		for(j=3*grow;j<3*grow+3;j++){
			if(SUDOKUarr[i][j]==num) return 0;
		}
	}
	for(i=0;i<9;i++){
		if(SUDOKUarr[i][y]==num||SUDOKUarr[x][i]==num){
			return 0;
		}
	}
	return 1;
}

int SUDOKUrightgong(int num,int glie,int grow){
	int i,j;
	for(i=3*glie;i<glie*3+3;i++){
		for(j=3*grow;j<3*grow+3;j++){
			if(SUDOKUarr[i][j]==num) return 0;
		}
	}
	return 1;
}
void SUDOKUset1(){
	int i,j,s,count=0,num;
	for(i=0;i<9;i++){
		for(j=0;j<9;j++) {
			if(SUDOKUarr[i][j]==0){
				for(s=1;s<=9;s++){
					if(SUDOKUright(s,i,j)){
						num = s;
						count++;
					}
				}
				if(count==1) {
					SUDOKUarr[i][j] = num;
				}
			}
		}
	}
}
void SUDOKUsetgong(int s,int glie,int grow){
	int i,j,count=0,x,y;
	for(i=3*glie;i<glie*3+3;i++){
		for(j=3*grow;j<3*grow+3;j++){
			if(SUDOKUright(s,i,j)) {
				count++;
				x = i;
				y = j;
			}
		}
	}
	if(count==1) {
		SUDOKUarr[x][y] = s;
	}
}
void SUDOKUset2(){
	int i,j,s;
	for(s=1;s<=9;s++){
		for(i=0;i<3;i++){
			for(j=0;j<3;j++) {
				if(SUDOKUrightgong(s,i,j)) SUDOKUsetgong(s,i,j);
			}
		}
	}
}
int SUDOKUrighthang(int s,int hang){
	int i;
	for(i=0;i<9;i++){
		if(SUDOKUarr[hang][i]==s){
			return 0;
		}
	}
	return 1;
}
int SUDOKUrightlie(int s,int lie){
	int i;
	for(i=0;i<9;i++){
		if(SUDOKUarr[i][lie]==s){
			return 0;
		}
	}
	return 1;
}
void SUDOKUsethang(int s,int hang){
	int i,count = 0;
	for(i=0;i<9;i++){
		if(SUDOKUright(s,hang,i)){
			count++;
		}
	}
	if(count==1){
		for(i=0;i<9;i++){
			if(SUDOKUright(s,hang,i)){
				SUDOKUarr[hang][i] = s;
				break;
			}
		}
	}
}
void SUDOKUsetlie(int s,int lie){
	int i,count = 0;
	for(i=0;i<9;i++){
		if(SUDOKUright(s,i,lie)){
			count++;
		}
	}
	if(count==1){
		for(i=0;i<9;i++){
			if(SUDOKUright(s,i,lie)){
				SUDOKUarr[i][lie] = s;
				break;
			}
		}
	}
}
void SUDOKUset3(){
	int i,j,s;
	for(s=1;s<=9;s++){
		for(i=0;i<9;i++){
			if(SUDOKUrighthang(s,i)){
				SUDOKUsethang(s,i);
			}
		}
	}
}
void SUDOKUset4(){
	int i,j,s;
	for(s=1;s<=9;s++){
		for(i=0;i<9;i++){
			if(SUDOKUrightlie(s,i)){
				SUDOKUsetlie(s,i);
			}
		}
	}
}
int SUDOKUgetnum(int i,int j){
	int s,count = 0;
	for(s=1;s<=9;s++){
		if(SUDOKUright(s,i,j)) count++;
	}
	return count; 
}
void SUDOKUsetone(int i,int j){
	int s;
	for(s=1;s<=9;s++){
		if(SUDOKUright(s,i,j)) {
			SUDOKUarr[i][j] = s;
			break;
		}
	}
}
void SUDOKUset5(){
	int i,j,s,count=0;
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			if(SUDOKUgetnum(i,j)==1) SUDOKUsetone(i,j);
		}
	}
}
void SUDOKUrun(){								//正推法,可以解决绝大多数题目而不用回溯法										
	int code[9][9],i,j;
rerun:
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			code[i][j] = SUDOKUarr[i][j];
		}
	}								
	SUDOKUset5();
	SUDOKUset4();
	SUDOKUset3();
	SUDOKUset2();
	SUDOKUset1();
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			if(code[i][j] != SUDOKUarr[i][j]) goto rerun;
		}
	}	
}																
int SUDOKUspace(){									//判断空格数,是否有错等判断方法 
	int i,j,count = 0;
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			if(SUDOKUarr[i][j]==0) count++;
		}
	}
	return count;
}

int SUDOKUfault(){
	int code[9]={0,0,0,0,0,0,0,0,0};
	int i,j;
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			code[SUDOKUarr[i][j]-1]++;
		}
	}
	for(i=0;i<9;i++){
		if(code[i]!=9) return 1;
		code[i] = 0;
	}
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			code[SUDOKUarr[i][j]-1]++;
		}
		for(j=0;j<9;j++){
			if(code[i]!=0) return 1;
		}
	}
	return 0;
}
int SUDOKUwrong(){
	int i,j;
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			if(SUDOKUarr[i][j]==0&&SUDOKUgetnum(i,j)==0)  return 1;
		}
	}
	return 0;
}
//下面是回溯法,解决数独问题
//检测行,列,宫是否填入正确(无重复) 
int SUDOKUcheck(int n,int m){	
	int i,j,glie = n/3,grow = m/3,num = SUDOKUarr[n][m];
	for(i=3*glie;i<glie*3+3;i++){
		for(j=3*grow;j<3*grow+3;j++){
			if(num==SUDOKUarr[i][j]&&!(i==n&&j==m)) return 0;
		}
	}
	for(i=0;i<9;i++){
		if((SUDOKUarr[i][m]==num&&i!=n)||(SUDOKUarr[n][i]==num&&i!=m))	return 0;
	}
	return 1;
}
//最主要方法,可解决所有问题:注意回溯和递归思路!!!
int SUDOKUsolve(){					 
	int i,j,k;
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			if(SUDOKUarr[i][j]!=0) continue;
			for(k=1;k<=9;k++){
				SUDOKUarr[i][j] = k;
				if(SUDOKUcheck(i,j)&&SUDOKUsolve()) return 1;
				SUDOKUarr[i][j] = 0;
			}
			return 0;
		}
	}
	return 1;
}
//正式完工(或许优化,加快速度什么的)
 
void sudoku(int ques[9][9]){
	int i,j;
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			SUDOKUarr[i][j] = ques[i][j];
		}
	}
	SUDOKUrun();
	if(SUDOKUspace()!=0) SUDOKUsolve();
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			ques[i][j] = SUDOKUarr[i][j];
		}
	}
}
#endif 

PS:上面写入头文件内部,下面是调用方法:

#include"SUDOKU.h"
#include<stdio.h>
int main(){
	int ques[9][9];
	printf("[请输入题目]\n");
	SUDOKUinput(ques);
	sudoku(ques);
	printf("[数独解决答案]\n"); 
	SUDOKUputout(ques);
} 

实例:
《数独C语言编程-正推与回溯》

点赞