LeetCode——解数独

9 篇文章 0 订阅
8 篇文章 0 订阅

Note:

  • 给定的数独序列只包含数字 1-9 和字符 '.' 。
  • 你可以假设给定的数独只有唯一解。
  • 给定数独永远是 9x9 形式的。

 在看到题目的第一个思路是通过深度优先搜索+优先队列+动态规划的方法解决

        根据三条规则找出每个空位可能的取值数量,通过优先队列选出可能数量最少的一个点进行填充,紧接着对优先队列进行更新,重新判断改变加入点后每个点不同的取值可能,接着按照DFS的思想对每个点进行依次填充,期间对于已经搜寻过的局势进行标记,再次出现时不进行搜索,直到所有点均被填充即为解。(要注意在DFS时全局变量的应用要格外小心!!!)

        最后做出的结果正确,但是执行时间及内存消耗都较大。下附提交结果图及程序代码:

  •  
  • class Solution {
    public:
    	struct Pos {
    		int x, y;	//坐标
    		//int depth;	//深度
    	};
    	struct Class {
    		Pos pos;	//坐标
    		int num;	//可能存在值的数量
    		bool possible[9] = { 0 };	//可能存在的值,0代表可取
    
    		bool operator <(const Class& s)const
    		{
    			if (num == s.num)	//可能性数量相同时按照坐标排序
    			{
    				if (pos.x == s.pos.x)
    					return pos.x > s.pos.x;		//
    				else
    					return pos.y > s.pos.y;		//
    			}
    			else
    				return num > s.num;		//最小堆
    		}
    	};
    	struct DT {
    		int arry[9][9];
    	};
    	map<string, int>DT_Flag;	//动态规划标志位,对每一种情况都有唯一的二维数组标识
    	map<int, DT>Source;		//表示应放回优先队列的深度及情况
    	//priority_queue<Class> Home;	//定义填写顺序
    	
    	int Gong[3][3][9] = { 0 };		//表征每个3×3宫里已经填写的数(1代表填写)
    	bool Finish = 0;
    	int High;
    
    	bool Updata(int h, priority_queue<Class> &updata)	//更新优先队列
    	{
    		for (int i = 0; i < 9; i++)
    		{
    			for (int j = 0; j < 9; j++)
    			{
    				if (Source[h].arry[i][j] == 0)
    				{
    					Class D;
    					D.pos.x = i;
    					D.pos.y = j;
    					D.num = 9;
    					for (int k = 0; k < 9; k++)
    					{
    						if (Source[h].arry[i][k] != 0)
    						{
    							if (D.possible[Source[h].arry[i][k] - 1] == 0)
    							{
    								D.possible[Source[h].arry[i][k] - 1] = 1;	//同行存在,改为不可取
    								D.num = D.num - 1;
    								//cout << i << "  " << j << ' ' << Source[h][i][k] << ' '<< D.possible[Source[h][i][k] -1]<<endl;
    							}
    						}
    					}
    					for (int k = 0; k < 9; k++)
    					{
    						if (Source[h].arry[k][j] != 0)
    						{
    							if (D.possible[Source[h].arry[k][j] - 1] == 0)
    							{
    								D.possible[Source[h].arry[k][j] - 1] = 1;	//同列存在,改为不可取
    								D.num = D.num - 1;
    								//cout << i << "  " << j << ' ' << Source[h][k][j] << ' ' << D.possible[Source[h][k][j] - 1] << endl;
    							}
    						}
    					}
    					int off_x = i / 3;
    					int off_y = j / 3;
    					for (int k = 0; k < 3; k++)
    					{
    						for (int p = 0; p < 3; p++)
    						{
    							if (Source[h].arry[k + off_x*3][p + off_y*3] != 0)	//同宫存在,改为不可取
    							{
    								if (D.possible[Source[h].arry[k + off_x * 3][p + off_y * 3] - 1] == 0)
    								{
    									D.possible[Source[h].arry[k + off_x * 3][p + off_y * 3] - 1] = 1;
    									D.num = D.num - 1;
    									//cout << i << "  " << j << ' ' << Source[h][k + off_x * 3][p + off_y * 3] << ' ' << D.possible[Source[h][k + off_x * 3][p + off_y * 3] - 1] << endl;
    								}
    							}
    						}
    					}
    					if (D.num == 0)		//有空但无值可取
    						return 0;		//代表该状态不可解
    					updata.push(D);
    				}
    			}
    		}
    		return 1;
    	}	
    
    	void DFS(int high)
    	{
    		priority_queue<Class> Home;
    		if (!Updata(high, Home))
    			return;
    		if (Home.empty())
    		{
    			High = high;
    			Finish = 1;
    		}
    		if (!Finish)
    		{
    			Class Save = Home.top();
    			for (int i = 0; i < Save.num; i++)
    			{
    				int j = 0;
    				while (Save.possible[j] == 1)
    				{
    					j++;
    				}
    				Save.possible[j] = 1;
    				//cout << "x=" << Save.pos.x << " y=" << Save.pos.y << " num = " << Save.num << " value = " << j + 1 << endl;
    				Source[0].arry[Save.pos.x][Save.pos.y] = j + 1;
    				
    				string dt;
    				for (int k = 0; k < 9; k++)
    				{
    					for (int p = 0; p < 9; p++)
    					{
    						char s[5] ;
    						sprintf_s(s, "%d", Source[0].arry[k][p]);
    						dt = dt.append(s);
    						//cout << "zifu = " << dt << endl;
    					}
    				}
    				
    				if (DT_Flag.find(dt) == DT_Flag.end())	//如果未检测该情况,则检测
    				{
    					DT_Flag[dt] = 1;
    					DFS(0);
    				}
    
    				if (!Finish)
    					Source[0].arry[Save.pos.x][Save.pos.y] = 0;
    				else
    					return;
    			}
    		}
    	}

    由于前期思路问题,优先队列其实没有必要,每次简单判断可能性即可。

  • 通过看网上大佬们的解题思路,发现自己在规则判定上效率很低,我的规则判断在每个点上,而大佬的判断在行、列、宫上,这样更加简单。

  • 经过改进后时间有显著减少

  • 结果图及程序代码如下:

lass Solution {
public:
	struct Class {
		int x,y;	//坐标
		int num;	//可能存在值的数量
		bool possible[9] = { 0 };	//可能存在的值,0代表可取
	};

	map<string, int>DT_Flag;	//动态规划标志位,对每一种情况都有唯一的二维数组标识
	int Source[9][9];		//表示应放回优先队列的深度及情况

	bool row[9][9] = { 0 };		//行填写数组
	bool col[9][9] = { 0 };		//列填写数组
	bool Gong[3][3][9] = { 0 };		//表征每个3×3宫里已经填写的数(1代表填写)

	bool Finish = 0;
	int release = 0;
	bool Updata(Class &Min)	//更新优先队列
	{
		Min.num = 10;
		for (int i = 0; i < 9; i++)
		{
			for (int j = 0; j < 9; j++)
			{
				Class CC;
				if (Source[i][j] == 0)
				{
					release++;
					CC.x = i;
					CC.y = j;
					CC.num = 9;
					for (int k = 0; k < 9; k++)
					{
						if (row[i][k] != 0 || col[j][k] != 0 || Gong[i / 3][j / 3][k] != 0)
						{
							CC.possible[k] = 1;	//同行、列、宫存在,改为不可取
							CC.num = CC.num - 1;
							//cout << i << "  " << j << ' ' << k + 1 << ' '<<endl;
						}
					}
					if (CC.num == 0)		//有空但无值可取
					{
						Min = CC;
						return 0;		//代表该状态不可解
					}
					else if (CC.num == 1)
					{
						Min = CC;
						return 1;
					}
					else if (CC.num < Min.num)
						Min = CC;
				}
			}
		}
		if (Min.num == 10)
		{
			release = 0;
			//cout << "end" << endl;
		}
		return 1;
	}	
	
	void DFS()
	{
		Class Min;
		if (!Updata(Min))
			return;
		if (release == 0)
		{
			Finish = 1;
		}
		if (!Finish)
		{
			for (int i = 0; i < Min.num; i++)
			{
				int j = 0;
				while (Min.possible[j] == 1)
				{
					j++;
				}
				Min.possible[j] = 1;
				//cout << "x=" << save.x << " y=" << save.y << " num = " << save.num << " value = " << j + 1 << endl;
				Source[Min.x][Min.y] = j + 1;
				row[Min.x][Source[Min.x][Min.y] - 1] = 1;		//更新
				col[Min.y][Source[Min.x][Min.y] - 1] = 1;
				Gong[Min.x / 3][Min.y / 3][Source[Min.x][Min.y] - 1] = 1;

				string dt;
				for (int k = 0; k < 9; k++)
				{
					for (int p = 0; p < 9; p++)
					{
						char s[5] ;
						sprintf(s, "%d", Source[k][p]);
						dt = dt.append(s);
					}
				}
				//cout << "zifu = " << dt << endl;
				
				if (DT_Flag.find(dt) == DT_Flag.end())	//如果未检测该情况,则检测
				{
					DT_Flag[dt] = 1;
					DFS();
				}

				if (!Finish)
				{
					Source[Min.x][Min.y] = 0;
					row[Min.x][j] = 0;		//更新
					col[Min.y][j] = 0;
					Gong[Min.x / 3][Min.y / 3][j] = 0;
				}
				else
					return;
			}
		}
	}

	void solveSudoku(vector<vector<char>>& board) {
		for (int i = 0; i < 9; i++)
		{
			for (int j = 0; j < 9; j++)
			{
				if(board[i][j] == '.')
					Source[i][j] = 0;
				else
				{
					Source[i][j] = board[i][j] - '0';
					row[i][Source[i][j] - 1] = 1;
					col[j][Source[i][j] - 1] = 1;
					Gong[i / 3][j / 3][Source[i][j] - 1] = 1;
				}
			}
		}
		DFS();
		for (int i = 0; i < 9; i++)
		{
			for (int j = 0; j < 9; j++)
			{

				board[i][j] = Source[i][j] + '0';
			}
		}
	}
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值