1. 程式人生 > >C語言數獨遊戲求解

C語言數獨遊戲求解

數獨遊戲的解法:

先將數獨分為九個格子,用一個數組將每個小九宮格的候選數存放下來,將候選數挨個放進數獨裡的空位,如果這一行和這一列都沒有這個數字,繼續放入下一個,如果不能放入的話就回到上一步繼續嘗試,直到成功求出數獨的解為止;


比如這個數獨第一個九宮格的候選數就有1,2,7,8,9,我們需要從1開始放入第一個格子挨個嘗試直到8的時候發現剩下的兩個格子都不能放入


這個時候我們就要撤回上一個插入的7,發現8仍然不能放入,就繼續撤回2,發現8可以放入,就將8放入3號位置,然後將9插入


這個時候我們發現2不能放入剩下的兩格,我們就繼續撤回到1插入的時候,將2放入1號位置,然後挨個放入剩下的數


迴圈這一過程,直到數獨求出解為止;

這個方法比較容易想到,操作也比較容易實現

下面是程式碼

程式碼大多數都寫了備註便於理解

題目需要的1000道題放在下面了,將這1000個txt檔案拷到EXE檔案同一目錄就可以了

題目連結:

連結:http://pan.baidu.com/s/1hsiUOqo 密碼:o9l5

#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
#define MAX 81
typedef struct asd{
	int x;//待測試的值的x座標 
	int y;//待測試的值的y座標  
	int p;//待測試的值的位置(1道9代表在九宮格里的位置) 
	int n;//待測試的值
}A;
A zhan[MAX];//存放每個放進題目陣列測試的資料 
void kongque(int queshi[9][9],int aa[9][9]);//函式將候選數數組裡去除題目中有的數字 
void shuchu(int aa[9][9],int q);//輸出整個陣列到檔案中 
int end(int aa[9][9]); //判斷是否結束 
int next(int queshi[9][9],int m,int n,int *x,int *y,int aa[9][9]);//查詢下一個應該放進九宮格測試的資料 
int chazhao(int aa[9][9],int m,int n,int num);//查詢同一行同一列是否有相同的值 
int nfrz(int queshi[9][9],int aa[9][9],int m,int n,int *p);//判斷是否滿足入棧條件(就是當前值是否可以插入九宮格) 
int rz(int *t,int x,int y,int p,int num);//入棧操作 
int cz(int *t,int *x,int *y,int *p,int *num);//出棧操作 
void aaaa(char aa[10],int a);//計算題目檔案的檔名 
void bbbb(char aa[10],int a);//計算答案檔案的檔名
int main(){
	int i;//記錄該呼叫哪道題 
	for(i=0;i<1000;i++){
		int aa[9][9],j,k;//aa陣列存放的是題目數獨 
		int queshi[9][9];//存放的是每個九宮格的待選數 
		int end=0;//判斷迴圈結束條件 
		int h=0,l=0,p=1;//h是候選數的行座標,l是候選數的列座標,p代表當前測試數屬於小九宮格的位置 
		int t=-1;//棧的長度 
		int s=0,num;
		FILE *u;
		char qwe[10];
		for(j=0;j<9;j++)//將陣列置為每行都是(1到9) 
			for(k=0;k<9;k++)
				queshi[j][k]=k+1;
		aaaa(qwe,i);
		u=fopen(qwe,"r");
		for(j=0;j<9;j++){//讀入題目 
			for(k=0;k<9;k++){
				fscanf(u,"%d",&aa[j][k]);
			}
		}
		fclose(u);
		memset(zhan,0,sizeof(zhan));//將棧的資料全部置為0 
		kongque(queshi,aa);
		while(end!=1){//開始求解 
			s=next(queshi,h,l,&h,&l,aa);//查詢下一個應該放進九宮格測試的資料
			if(s==0){//如果找到則進入下一層 
				s=nfrz(queshi,aa,h,l,&p);//判斷能否插入數獨裡 
				if(s==0){//如果可以則將插入的資料存放到棧裡(入棧) 
					s=rz(&t,h,l,p,queshi[h][l]);
					if(s==0){ //如果入棧成功則寫入數獨
						aa[h/3*3+(p-1)/3][h%3*3+(p-1)%3]=queshi[h][l];
						l++;//待選數跳到下一個 
						p=1;//重新從第一個小格子開始判斷是否插入 
					}
					else{
						end=1;//迴圈結束 
					}
				}
				else{
					s=cz(&t,&h,&l,&p,&num);
					if(s==0){//如果出棧成功則擦除插入的資料 
						aa[h/3*3+(p-1)/3][h%3*3+(p-1)%3]=0;
						p++;
					}
					else
						end=1;
				}
			}
			else if(s==-1){
				shuchu(aa,i);//輸出求解完畢的數獨 
				end=1; 
			}
			else{
				printf("發生未知錯誤");
				end=1;
			}
		}
	}
	return 0;
}
//函式將候選數數組裡去除題目中有的數字 
void kongque(int queshi[9][9],int aa[9][9]){
	int i,j,x,y;
	for(i=0;i<j;i++){
		for(j=0;j<9;j++){
			if(aa[i][j]){
				x=i/3*3+j/3;//數獨陣列和候選數陣列的座標轉換 
				y=aa[i][j]-1;
				queshi[x][y]=0;
			}
		}
	}
}
//輸出整個陣列到檔案中
void shuchu(int aa[9][9],int q){
	int i,j;
	FILE *p;
	char qq[10];
	bbbb(qq,q);
	p=fopen(qq,"w");
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			fprintf(p,"%d ",aa[i][j]);
		}
		fprintf(p,"\n");
	}
	fclose(p);
}
//判斷是否結束
int end(int aa[9][9]){
	int i,j,num=0;
	for(i=0;i<9;i++){
		num=0;
		for(j=0;j<0;j++){
			num+=aa[i][j];//檢查每一行是否為1到9 
		}
		if(num!=45)
			return -1;
	}
	for(j=0;j<9;j++){//檢查每一列是否為1到9 
		num=0;
		for(i=0;i<9;i++){
			num+=aa[i][j];
		}
		if(num!=45)
			return -1;
	}
	return 0;
}
//查詢下一個應該放進九宮格測試的資料 
int next(int queshi[9][9],int m,int n,int *x,int *y,int aa[9][9]){
	int qqq=0;
	if(n>8){//如果當前小九宮格填寫完畢則進入下一個九宮格 
		n=0;
		m++;
	}
	if(m>8){
		qqq=end(aa);//判斷是否結束 
		if(qqq!=0)
			return -1;
		else
			return 1;
	}
	while(queshi[m][n]==0){
		if(n<8)
			n++;
		else{
			n=0;
			m++;
			if(m>8){
				qqq=end(aa);
				if(qqq!=0)
					return -1;
				else
					return 1;
			}
		}
	}
	*x=m;//重新獲取測試的值的x座標和y座標 
	*y=n;
	return 0;
}
//查詢同一行同一列是否有相同的值 
int chazhao(int aa[9][9],int m,int n,int num){
	int i;
	for(i=0;i<9;i++){//查詢行 
		if(aa[m][i]==num)
			return -1;
	}
	for(i=0;i<9;i++){//查詢列 
		if(aa[i][n]==num)
			return -1;
	}
	return 0;
}
//判斷是否滿足入棧條件(就是當前值是否可以插入九宮格) 
int nfrz(int queshi[9][9],int aa[9][9],int m,int n,int *p){
	int s=*p;
	int i,t1,t2,num;
	num=queshi[m][n];
	for(i=s;i<10;i++){
		t1=(m/3)*3+(s-1)/3;
		t2=(m%3)*3+(s-1)%3;
		if(aa[t1][t2]!=0){
			s++;
			continue;
		}
		if(chazhao(aa,t1,t2,num)!=0){
			s++;
			continue;
		}
		else{
			*p=s;
			return 0;
		}
	}
	return -1;
}
//入棧操作 
int rz(int *t,int x,int y,int p,int num){
	if(*t>=MAX){
		return -1;
	}
	else{
		(*t)++;
		zhan[*t].x=x;
		zhan[*t].y=y;
		zhan[*t].p=p;
		zhan[*t].n=num;
		return 0;
	}
}
//出棧操作 
int cz(int *t,int *x,int *y,int *p,int *num){
	if(*t==-1){
		return -1;
	}
	else{
		*x=zhan[*t].x;
		*y=zhan[*t].y;
		*p=zhan[*t].p;
		*num=zhan[*t].n;
		(*t)--;
		return 0;
	}
}
//計算題目檔案的檔名 
void aaaa(char aa[10],int a){
	if(a>=0&&a<10){
		aa[0]='0';
		aa[1]='0';
		aa[2]='0';
		aa[3]=a+'0';
	}
	else if(a<100){
		aa[0]='0';
		aa[1]='0';
		aa[2]=a/10+'0';
		aa[3]=a%10+'0';
	}
	else if(a<1000){
		aa[0]='0';
		aa[1]=a/100+'0';
		aa[2]=a/10%10+'0';
		aa[3]=a%10+'0';
	}
	aa[4]='.';
	aa[5]='t';
	aa[6]='x';
	aa[7]='t';
	aa[8]='\0';
}
//計算答案檔案的檔名
void bbbb(char aa[10],int a){
	if(a>=0&&a<10){
		aa[0]='a';
		aa[1]='0';
		aa[2]='0';
		aa[3]=a+'0';
	}
	else if(a<100){
		aa[0]='a';
		aa[1]='0';
		aa[2]=a/10+'0';
		aa[3]=a%10+'0';
	}
	else if(a<1000){
		aa[0]='a';
		aa[1]=a/100+'0';
		aa[2]=a/10%10+'0';
		aa[3]=a%10+'0';
	}
	aa[4]='.';
	aa[5]='t';
	aa[6]='x';
	aa[7]='t';
	aa[8]='\0';
}