[迭代][DFS]用C++求解传统九宫格数独

简介

本程序先通过迭代推算出部分数字,然后通过DFS搜索解空间,迭代时排除了一部分不可能解以加速遍历

流程图

Created with Raphaël 2.2.0 开始 输入sodu矩阵并初始化possibility[9][9][9] 输入是否符合规则? 根据现有sodu矩阵找到行列或九宫格内的唯一项 排除possibility[9][9][9]中的不可能项 根据possibility[9][9][9]填充确定项并维护possibility[9][9][9] 是否解出所有项? 结束 上次迭代是否有更新? 使用DFS搜索解空间 yes no yes no yes no

部分代码解析

全局变量

//sodu矩阵用于接收sudo谜题,且一切求解操作都作用于此
char sodu[9][9];
//possibility[x][y][z]代表了sodu矩阵中第x行,第y列,z是否可能存在
bool possibility[9][9][9];
//哈希表,在迭代完成后,标记所有空白,用于在遍历解空间时可以根据遍历深度快速找到目标空白
char hash[82][2];

函数功能描述

/*从文件读取sudo矩阵,并且初始化possibility矩阵*/
void EnterAndInitial()

/*在sudo数组中,各行各列各九宫格中,根据possibility矩阵寻找可以唯一确定的空白格*/
void GetUniquity()

/*用于维护possibility矩阵*/
void DiminishPossibility()

/*根据possibility矩阵,重新填充sodu矩阵,并且判断本次迭代是否有新的产出及是否求解完毕*/
int NextState()

/*迭代,并且判断迭代是否可以完成求解*/
bool IsCalculatable()

/*用于判断某矩阵是否违背sodu规则*/
bool IsRational()

/*深度优先搜索,并判断是否存在解*/
bool DFS(char depth)

迭代

bool IsCalculatable(){
	int ptr = 0;
	char rest = 81, lastStepRest = 81;
	//如果迭代至没有空白块,则本sudo谜题已解决
	while(rest>0){
		//z389x56y4,若y不可能为7,x不可能为7,则z=7
		GetUniquity();
		//z389x56y4,可知xyz都不可能为3
		DiminishPossibility();
		//找到只有一个可能的单元格,填充,计算剩下几个空白块
		rest = NextState();
		//如果上一次剩余空白块和本次迭代剩余空白块数量相等,则继续迭代无效,需要搜索解空间
		if(lastStepRest != rest) lastStepRest = rest;
		else{
			//在进行DFS前,先将所有空白块标记进入hash表
			for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++) if(!sodu[cnt1][cnt2]){hash[ptr][0]=cnt1; hash[ptr++][1]=cnt2;}
			hash[ptr][0] = -1; hash[ptr][1] = -1;
			return false;
		}
	}
	return true;
}

DFS

bool DFS(char depth){
	//递归基,达到递归基则已找到解,因为在迭代完成后,已经把哈希表的结尾用-1标记
	if(hash[depth][0] == -1) return true;
	else{
		for(int cnt1=1; cnt1<=9; cnt1++){
			//如果cnt1在当前空白块可能存在(根据possibility[9][9][9]判断)
			if(possibility[hash[depth][0]][hash[depth][1]][cnt1-1]){
				sodu[hash[depth][0]][hash[depth][1]] = cnt1;
				//假设此空白块为cnt1,若不违背数独规则
				//则继续depth+1
				if(IsRational()&&DFS(depth+1)) return true;
				//以上任一条件不满足,则此块再次置空
				else sodu[hash[depth][0]][hash[depth][1]] = 0;
			}
		}
		return false;
	}
}

全部代码

#include <iostream>
#include <fstream>
using namespace std;
char sodu[9][9];
bool possibility[9][9][9];
char hash[82][2];
void EnterAndInitial(){
	ifstream input("sodu.txt");
	for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++){
		input >> sodu[cnt1][cnt2]; 
		sodu[cnt1][cnt2] -= '0';
		if(sodu[cnt1][cnt2]){
			for(int cnt3=0; cnt3<9; cnt3++) possibility[cnt1][cnt2][cnt3] = false;
			possibility[cnt1][cnt2][sodu[cnt1][cnt2]-1] = true;
		}
		else
			for(int cnt3=0; cnt3<9; cnt3++) possibility[cnt1][cnt2][cnt3] = true;
	}
}
void GetUniquity(){
	int temp1, temp2, temp3, x, y;
	for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++){
		temp1 = 0;
		for(int cnt3=0; cnt3<9; cnt3++) if(possibility[cnt1][cnt3][cnt2]){temp1++; temp2=cnt3;}
		if(temp1 == 1){
			sodu[cnt1][temp2] = cnt2+1;
			for(int cnt3=0; cnt3<9; cnt3++) possibility[cnt1][temp2][cnt3] = false;
			possibility[cnt1][temp2][cnt2] = true;
		}
	}
	for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++){
		temp1 = 0;
		for(int cnt3=0; cnt3<9; cnt3++) if(possibility[cnt3][cnt1][cnt2]){temp1++; temp2=cnt3;}
		if(temp1 == 1){
			sodu[temp2][cnt1] = cnt2+1;
			for(int cnt3=0; cnt3<9; cnt3++) possibility[temp2][cnt1][cnt3] = false;
			possibility[temp2][cnt1][cnt2] = true;
		}
	}
	for(x=0; x<9; x+=3) for(y=0; y<9; y+=3){
		for(int cnt1=0; cnt1<9; cnt1++){
			temp1 = 0;
			for(int cnt2=0; cnt2<3; cnt2++) for(int cnt3=0; cnt3<3; cnt3++) if(possibility[cnt2+y][cnt3+x][cnt1]){temp1++; temp2=cnt2; temp3=cnt3;}
			if(temp1 == 1){
				sodu[temp2+y][temp3+x] = cnt1+1;
				for(int cnt3=0; cnt3<9; cnt3++) possibility[temp2+y][temp3+x][cnt3] = false;
				possibility[temp2+y][temp3+x][cnt1] = true;
			}
		}
	}
}
void DiminishPossibility(){
	int x, y; 
	for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++){
		if(sodu[cnt1][cnt2]){
			if(cnt1<3) y=0; else if(cnt1 < 6) y=3; else y=6;
			if(cnt2<3) x=0; else if(cnt2 < 6) x=3; else x=6;
			for(int cnt3=0; cnt3<9; cnt3++){
				possibility[cnt1][cnt3][sodu[cnt1][cnt2]-1] = false;
				possibility[cnt3][cnt2][sodu[cnt1][cnt2]-1] = false;
			}
			for(int cnt3=0; cnt3<3; cnt3++) for(int cnt4=0; cnt4<3; cnt4++) possibility[cnt3+y][cnt4+x][sodu[cnt1][cnt2]-1] = false;
		} 
	}
}
int NextState(){
	int amountOfPossibility;
	int temp1;
	int rest = 81;
	for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++){
		amountOfPossibility = 0;
		for(int cnt3=0; cnt3<9; cnt3++){
			if(possibility[cnt1][cnt2][cnt3]){
				amountOfPossibility++;
				temp1 = cnt3+1;
			}
		}
		if(amountOfPossibility == 1) sodu[cnt1][cnt2] = temp1;
		if(sodu[cnt1][cnt2]) rest--;
	}
	return rest;
}
bool IsCalculatable(){
	int ptr = 0;
	char rest = 81, lastStepRest = 81;
	while(rest>0){
		GetUniquity();
		DiminishPossibility();
		rest = NextState();
		if(lastStepRest != rest) lastStepRest = rest;
		else{
			for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++) if(!sodu[cnt1][cnt2]){hash[ptr][0]=cnt1; hash[ptr++][1]=cnt2;}
			hash[ptr][0] = -1; hash[ptr][1] = -1;
			return false;
		}
	}
	return true;
}
bool IsRational(){
	int temp, x, y;
	for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=1; cnt2<=9; cnt2++) for(int cnt3=0, temp=0; cnt3<9; cnt3++){
		if(sodu[cnt1][cnt3] == cnt2) temp++;
		if (temp > 1) return false;
	}
	for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=1; cnt2<=9; cnt2++) for(int cnt3=0, temp=0; cnt3<9; cnt3++){
		if(sodu[cnt3][cnt1] == cnt2) temp++;
		if (temp > 1) return false;
	}
	for(x=0; x<9; x+=3) for(y=0; y<9; y+=3)
		for(int cnt1=1; cnt1<=9; cnt1++){
			temp=0;
			for(int cnt2=0; cnt2<3; cnt2++) for(int cnt3=0; cnt3<3; cnt3++){
				if(sodu[cnt2+x][cnt3+y] == cnt1) temp++;
				if(temp > 1) return false;
			}
		}
	return true;
}
bool DFS(char depth){
	if(hash[depth][0] == -1) return true;
	else{
		for(int cnt1=1; cnt1<=9; cnt1++){
			if(possibility[hash[depth][0]][hash[depth][1]][cnt1-1]){
				sodu[hash[depth][0]][hash[depth][1]] = cnt1;
				if(IsRational()&&DFS(depth+1)) return true;
				else sodu[hash[depth][0]][hash[depth][1]] = 0;
			}
		}
		return false;
	}
}
bool Solve(){
	if(IsRational())
		if(IsCalculatable()||DFS(0)) return true; else return false;
	else return false;
}
void Output(bool solvable){
	if(solvable){
		for(int cnt1=0; cnt1<9; cnt1++){
			for(int cnt2=0; cnt2<9; cnt2++){
				sodu[cnt1][cnt2] += '0';
				cout << sodu[cnt1][cnt2] << ' ';
			}
			cout << "\n";
		}
	}
	else cout << "unsolvable!";
}
int main(){
	EnterAndInitial();
	Output(Solve());
	return 0;
}

测试样例

8 0 0 0 0 0 0 0 0
0 0 3 6 0 0 0 0 0
0 7 0 0 9 0 2 0 0
0 5 0 0 0 7 0 0 0
0 0 0 0 4 5 7 0 0
0 0 0 1 0 0 0 3 0
0 0 1 0 0 0 0 6 8
0 0 8 5 0 0 0 1 0
0 9 0 0 0 0 4 0 0
本题目来源于号称“世界上迄今难度最大的数独游戏”

CPU Intel i5-7300HQ
Memory 8.00GB
System Windows 10 Professional

在这里插入图片描述

改进余地

1.本人对数独技巧不很精通,通过迭代能找出的空白较少,使用更高级的技巧进行迭代则可以进一步缩小DFS的解空间
2.程序只能找出一个解(存在很多解的数独谜题是存在的),因为本程序一旦找到一个解就不继续寻找了

有问题欢迎提出

发布了1 篇原创文章 · 获赞 1 · 访问量 42

猜你喜欢

转载自blog.csdn.net/TZR986981442/article/details/104415967