【回溯 状态压缩 深度优先】37. 解数独

本文涉及知识点

回溯 状态压缩 深度优先

LeetCode37. 解数独

编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

示例 1:
输入:board = [[“5”,“3”,“.”,“.”,“7”,“.”,“.”,“.”,“.”],[“6”,“.”,“.”,“1”,“9”,“5”,“.”,“.”,“.”],[“.”,“9”,“8”,“.”,“.”,“.”,“.”,“6”,“.”],[“8”,“.”,“.”,“.”,“6”,“.”,“.”,“.”,“3”],[“4”,“.”,“.”,“8”,“.”,“3”,“.”,“.”,“1”],[“7”,“.”,“.”,“.”,“2”,“.”,“.”,“.”,“6”],[“.”,“6”,“.”,“.”,“.”,“.”,“2”,“8”,“.”],[“.”,“.”,“.”,“4”,“1”,“9”,“.”,“.”,“5”],[“.”,“.”,“.”,“.”,“8”,“.”,“.”,“7”,“9”]]
在这里插入图片描述

输出:[[“5”,“3”,“4”,“6”,“7”,“8”,“9”,“1”,“2”],[“6”,“7”,“2”,“1”,“9”,“5”,“3”,“4”,“8”],[“1”,“9”,“8”,“3”,“4”,“2”,“5”,“6”,“7”],[“8”,“5”,“9”,“7”,“6”,“1”,“4”,“2”,“3”],[“4”,“2”,“6”,“8”,“5”,“3”,“7”,“9”,“1”],[“7”,“1”,“3”,“9”,“2”,“4”,“8”,“5”,“6”],[“9”,“6”,“1”,“5”,“3”,“7”,“2”,“8”,“4”],[“2”,“8”,“7”,“4”,“1”,“9”,“6”,“3”,“5”],[“3”,“4”,“5”,“2”,“8”,“6”,“1”,“7”,“9”]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
提示:
在这里插入图片描述

board.length == 9
board[i].length == 9
board[i][j] 是一位数字或者 ‘.’
题目数据 保证 输入数独仅有一个解

回溯

vRow[i] 记录第i行可以选择那些数,vCol[i]和vCell类型。
vRow[i] & ( 1 << j) 表示第i行,可以选择数组j。
直接将选择结果修改到board上。
vector<tuple<int,int,int>> vSel。 i1,记录可以选择的数量,i2记录行号,i3记录列号。注意:只需要记录能修改的数组。 初始化结束后,对vSel排序。理论上:只有一种选择的先选快点。实际上几乎无影响。
用深度优先实现。Fill 函数填写某行某列,UnFill 恢复某行某列原装。
时间复杂度:不好计算。

代码

核心代码

class CBitCounts
{
public:
	CBitCounts(int iMaskCount)
	{
		for (int i = 0; i < iMaskCount; i++)
		{
			m_vCnt.emplace_back(bitcount(i));
		}
	}
	template<class T>
	static int bitcount(T x) {
		int countx = 0;
		while (x) {
			countx++;
			x &= (x - 1);
		}
		return countx;
	}
	vector<int> m_vCnt;
};

class Solution {
public:
	void solveSudoku(vector<vector<char>>& board) {
		m_board = board;
		int mask = 0;
		for (int i = 1; i <= 9; i++) {
			mask |= (1 << i);
		}
		for (int i = 0; i < 9; i++) {
			m_rows[i] = m_cols[i] = m_cells[i] = mask;
		}
		
		for (int r = 0; r < 9; r++) {
			for (int c = 0; c < 9; c++) {
				if ('.' == board[r][c]) { continue; }
				Fill(r, c, board[r][c] - '0');
			}
		}
		vector<tuple<int, int, int,int>> vSel;
		for (int r = 0; r < 9; r++) {
			for (int c = 0; c < 9; c++) {
				if ('.' != board[r][c]) { continue; }
				int iCell = r / 3 * 3 + c / 3;
				int mask = m_rows[r] & m_cols[c] & m_cells[iCell];
				vSel.emplace_back(CBitCounts::bitcount(mask), r, c,iCell);
			}
		}
		sort(vSel.begin(), vSel.end());
		DFS(vSel, 0);
		board = m_board;
	}
	bool DFS(const vector<tuple<int, int, int,int>> vSel, int leve) {
		if (vSel.size() == leve) { return true; }
		const auto& [tmp, r, c, iCell] = vSel[leve];
		int mask = m_rows[r] & m_cols[c] & m_cells[iCell];
		for (int i = 1; i <= 9; i++) {
			if (mask & (1 << i)) {
				Fill(r, c, i);
				if (DFS(vSel, leve + 1)) { return true; }
				UnFill(r, c, i);
			}
		}
		return false;
	}
	void Fill (int r, int c, int val) {
		m_board[r][c] = val + '0';
		m_rows[r] &= ~(1 << val);
		m_cols[c] &= ~(1 << val);
		int iCell = r / 3 * 3 + c / 3;
		m_cells[iCell] &= ~(1 << val);
	};
	void UnFill(int r, int c, int val) {
		m_board[r][c] = '.';
		m_rows[r] |= (1 << val);
		m_cols[c] |= (1 << val);
		int iCell = r / 3 * 3 + c / 3;
		m_cells[iCell] |= (1 << val);
	};
	vector<vector<char>> m_board;
	int m_rows[9], m_cols[9], m_cells[9];
};

测试用例

template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
	if (v1.size() != v2.size())
	{
		assert(false);
		return;
	}
	for (int i = 0; i < v1.size(); i++)
	{
		assert(v1[i] == v2[i]);
	}
}

template<class T>
void Assert(const T& t1, const T& t2)
{
	assert(t1 == t2);
}

int main()
{
	vector<vector<char>> board;
	{
		Solution slu;
		board ={ {'5', '3', '.', '.', '7', '.', '.', '.', '.'}, { '6','.','.','1','9','5','.','.','.' }, { '.','9','8','.','.','.','.','6','.' }, { '8','.','.','.','6','.','.','.','3' }, { '4','.','.','8','.','3','.','.','1' }, { '7','.','.','.','2','.','.','.','6' }, { '.','6','.','.','.','.','2','8','.' }, { '.','.','.','4','1','9','.','.','5' }, { '.','.','.','.','8','.','.','7','9' }};
		 slu.solveSudoku(board);
		 vector<vector<char>> board1={ {'5', '3', '4', '6', '7', '8', '9', '1', '2'}, { '6','7','2','1','9','5','3','4','8' }, { '1','9','8','3','4','2','5','6','7' }, { '8','5','9','7','6','1','4','2','3' }, { '4','2','6','8','5','3','7','9','1' }, { '7','1','3','9','2','4','8','5','6' }, { '9','6','1','5','3','7','2','8','4' }, { '2','8','7','4','1','9','6','3','5' }, { '3','4','5','2','8','6','1','7','9' }};
		Assert(board1, board);
	}	
}

2023年5月代码

记录已经选择的数,这样初始化简单。用二维数组记录3 × \times × 3 网格的情况,减少计算网格号。

class Solution {
public:
	void solveSudoku(vector<vector<char>>& board) {
		memset(m_aRows, 0, sizeof(m_aRows));
		memset(m_aCols, 0, sizeof(m_aCols));
		memset(m_aBlock, 0, sizeof(m_aBlock));
		
		for (int r = 0; r < 9; r++)
		{
			for (int c = 0; c < 9; c++)
			{
				const char& ch = board[r][c];
				if ('.' == ch)
				{
					m_vNeedDoRowCols.emplace_back(r, c);
					continue;
				}
				Add(r, c, ch - '1');
			}
		}
		dfs(board, 0);
	}
	bool dfs(vector<vector<char>>& board,int iLeve)
	{
		if (m_vNeedDoRowCols.size() == iLeve)
		{
			return true;
		}
		const int r = m_vNeedDoRowCols[iLeve].first;
		const int c = m_vNeedDoRowCols[iLeve].second;
		int iMask = m_aRows[r] | m_aCols[c] | m_aBlock[r/3][c/3];
		for (int i = 0; i < 9; i++)
		{
			if (iMask & (1 << i))
			{
				continue;
			}
			Add(r, c, i);
			board[r][c] = '1' + i;
			if (dfs(board, iLeve + 1))
			{
				return true;
			}
			board[r][c] = '.';
			Erase(r, c, i);
		}
		return false;
	}
	void Add(int r, int c, int iNum)
	{
		const int iMask = 1 << iNum;
		m_aRows[r] |= iMask;
		m_aCols[c] |= iMask;
		m_aBlock[r / 3][c / 3] |= iMask;
	}
	void Erase(int r, int c, int iNum)
	{
		const int iMask = 1 << iNum;
		m_aRows[r] -= iMask;
		m_aCols[c] -= iMask;
		m_aBlock[r / 3][c / 3] -= iMask;
	}
	int m_aRows[9],m_aCols[9];
	int m_aBlock[3][3];
	vector<std::pair<int, int>> m_vNeedDoRowCols;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/612439.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3d如何同时贴两个图在模型上?---模大狮模型网

在3D设计中&#xff0c;为模型贴上纹理或图案是常见的操作&#xff0c;可以使模型更加逼真和生动。然而&#xff0c;有时候我们需要在同一个模型上同时贴上两个不同的图案&#xff0c;这可能会对初学者构成一定的挑战。在本文中&#xff0c;我们将分享一些简单而有效的方法&…

基于SSM框架多人命题系统

采用技术 基于SSM框架多人命题系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringMVCMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 学生端 登录 个人中心 公告信息 试题信息 管理员 登录 个人信息…

阳光厨房/明厨亮灶解决方案

现状分析 随着社会和科技的进步&#xff0c;日益增多的食品安全问题&#xff0c;国家四部委市场监督管理总局、教育部、公安部、国家卫生健康委联合印发《校园食品安全守护行动方案&#xff08;2020年—2022年&#xff09;》“互联网明厨亮灶”工程号召&#xff0c;对食品、餐饮…

机器学习-L1正则/L2正则

机器学习-L1正则/L2正则 目录 1.L1正则 2.L2正则 3.结合 1.L1正则 L1正则是一种用来约束模型参数的技术&#xff0c;常用于机器学习和统计建模中&#xff0c;特别是在处理特征选择问题时非常有用。 想象一下&#xff0c;你在装备行囊准备去旅行&#xff0c;但你的行囊有一…

详解Python测试框架Pytest的参数化

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 上篇博文介绍过&#xff0c;Pytest是目前比较成熟功能齐全的测试框架&#xff0c;使用率肯定也不…

【深度学习】Diffusion扩散模型原理解析2

由于篇幅受限&#xff0c;CSDN不能发布超过一定次数的文章&#xff0c;故在此给出上一篇链接&#xff1a;【深度学习】diffusion原理解析 3.2、目标函数求解 里面的最后一项&#xff0c; q ( x T ∣ x 0 ) q(x_T|x_0) q(xT​∣x0​)我们前面提到过&#xff0c;其近似服从标准…

flowable多对并发网关跳转的分析

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://218.75.87.38:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; h…

C++:虚函数表Hook

Hook 在计算机编程中&#xff0c;"Hook"&#xff08;钩子&#xff09;是一种技术&#xff0c;用于拦截并修改特定事件或函数的执行流程。它允许程序员在特定的代码点插入自定义的代码&#xff0c;以实现对程序行为的修改、监视或增强。 虚函数表Hook 虚函数表&#…

控制台打印空数组展开有数据

控制台打印空数组展开有数据 控制台显示&#xff1a; 代码如下&#xff1a; export const getDict1 (dictCode) > {let list []queryDict({ dictCode }).then(({data}) > {list.push( ...data.map(item > ({ label: item.itemText, value: item.itemValue })))})c…

目标检测算法YOLOv7简介

YOLOv7由Chien-Yao Wang等人于2022年提出&#xff0c;论文名为&#xff1a;《YOLOv7: Trainable bag-of-freebies sets new state-of-the-art for real-time object detectors》&#xff0c;论文见&#xff1a;https://arxiv.org/pdf/2207.02696 &#xff0c;项目网页&#xff…

解决 git 因输入密码错误而导致的报错无法推送问题

报错内容如下&#xff1a; > git push origin master:master fatal: unable to access https://gitee.com/spring-in-huangxian-county/web-tts-vue.git/: OpenSSL SSL_connect: Connection was reset in connection to gitee.com:443 出错原因 根本原因是本机存储的 账户…

大型动作模型 (LAM):AI 驱动的交互的下一个前沿

1.概述 现在人工智能中几个关键的领域&#xff0c;包括生成式人工智能&#xff08;Generative AI&#xff09;、大型动作模型&#xff08;Large Action Models, LAM&#xff09;、以及交互式人工智能&#xff08;Interactive AI&#xff09;。以下是对这些概念的简要解释和它们…

数据库管理-第187期 23ai:怎么用SQL创建图(20240510)

数据库管理187期 2024-05-10 数据库管理-第187期 23ai:怎么用SQL创建图&#xff08;20240510&#xff09;1 安装PGX1.1 数据库配置对应用户1.2 使用RPM包安装Graph Server1.3 安装Oracle Graph Client1.4 访问PGX页面 2 SQL Property Graph2.1 创建SQL属性图2.2 关于点和边图元…

c++11 标准模板(STL)本地化库 - 平面类别(std::money_put) - 格式化货币值为字符序列以输出

本地化库 本地环境设施包含字符分类和字符串校对、数值、货币及日期/时间格式化和分析&#xff0c;以及消息取得的国际化支持。本地环境设置控制流 I/O 、正则表达式库和 C 标准库的其他组件的行为。 平面类别 格式化货币值为字符序列以输出 std::money_put template< …

聊聊ChatGPT:智能语言模型背后的原理

目录 1. ChatGPT的基础&#xff1a;GPT模型 2. 预训练与微调&#xff1a;让模型更加智能 2.1 预训练 2.2 微调 3. 多样化的应用场景 4. 未来的展望 5. 结语 在当今的人工智能领域&#xff0c;OpenAI的ChatGPT无疑是一个炙手可热的话题。它不仅能流畅地进行对话&#xff…

【ArcGISProSDK】condition属性

示例 通过caption属性可以看出esri_mapping_openProjectCondition的条件是一个工程被打开 condition的作用 由此可知示例中的Tab实在工程被打开才能使用&#xff0c;否则他禁用显示灰色&#xff0c;在未禁用的时候说明条件满足。 参考文档 insertCondition 元素 (arcgis.com…

局域网手机端远程控制手机

局域网手机端远程控制手机 随着科技的进步和智能设备的普及&#xff0c;远程控制技术在日常生活与工作中的应用越来越广泛。其中&#xff0c;局域网内的手机端远程控制手机技术&#xff0c;因其便捷性和实用性&#xff0c;受到了众多用户的关注。本文将简要介绍该技术及其应用…

#兼职副业赚钱吗?# 宝妈与上班族在水牛社的财富探索

在这个繁忙的都市节奏中&#xff0c;宝妈与上班族都面临着平衡家庭与经济的挑战。那么&#xff0c;兼职副业真的能为他们带来额外的收入吗&#xff1f;接下来&#xff0c;让我们通过两个实例&#xff0c;揭示宝妈和上班族是如何在水牛社找到兼职副业赚钱的契机的。 ✨ 宝妈的故…

Prompt|Kimi高阶技巧,99%的人都不知道

大家好&#xff0c;我是无界生长。 今天分享一条咒语&#xff0c;轻松让Kimi帮你生成流程图&#xff0c;学会了的话&#xff0c;点赞收藏起来吧&#xff01; 效果展示 我们演示一下让kimi帮忙绘制 关注微信公众号“无界生长”的流程图&#xff0c;最终效果图如下所示 效果还不…

Dijkstra求最短路 I:图解 详细代码(图解)

文章目录 题目&#xff1a;Dijkstra求最短路思路伪代码&#xff1a;代码优化优化代码&#xff1a;Java代码 总结 题目&#xff1a;Dijkstra求最短路 给定一个 n个点 m条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;所有边权均为正值。 请你求出 1号点到 n号点的…