每周一算法:数独游戏

题目链接

数独游戏

题目描述

数独是根据 9 × 9 9 \times 9 9×9 盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫内的数字均含 1 − 9 1 - 9 19 ,不重复。每一道合格的数独谜题都有且仅有唯一答案,推理方法也以此为基础,任何无解或多解的题目都是不合格的。

芬兰一位数学家号称设计出全球最难的“数独游戏”,并刊登在报纸上,让大家去挑战。

这位数学家说,他相信只有“智慧最顶尖”的人才有可能破解这个“数独之谜”。

据介绍,目前数独游戏的难度的等级有一到五级,一是入门等级,五则比较难。不过这位数学家说,他所设计的数独游戏难度等级是十一,可以说是所以数独游戏中,难度最高的等级。他还表示,他目前还没遇到解不出来的数独游戏,因此他认为“最具挑战性”的数独游戏并没有出现。

输入格式

一个未填的数独。

输出格式

填好的数独。

样例 #1

样例输入 #1

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

样例输出 #1

8 1 2 7 5 3 6 4 9 
9 4 3 6 8 2 1 7 5 
6 7 5 4 9 1 2 8 3 
1 5 4 2 3 7 8 9 6 
3 6 9 8 4 5 7 2 1 
2 8 7 1 6 9 5 3 4 
5 2 1 9 7 4 3 6 8 
4 3 8 5 2 6 9 1 7 
7 9 6 3 1 8 4 5 2

样例输入 #2

9 0 0 8 0 0 0 0 0
0 0 0 0 0 0 5 0 0 
0 0 0 0 0 0 0 0 0 
0 2 0 0 1 0 0 0 3
0 1 0 0 0 0 0 6 0
0 0 0 4 0 0 0 7 0
7 0 8 6 0 0 0 0 0 
0 0 0 0 3 0 1 0 0 
4 0 0 0 0 0 2 0 0 

样例输出 #2

9 7 2 8 5 3 6 1 4 
1 4 6 2 7 9 5 3 8 
5 8 3 1 4 6 7 2 9 
6 2 4 7 1 8 9 5 3 
8 1 7 3 9 5 4 6 2 
3 5 9 4 6 2 8 7 1 
7 9 8 6 2 1 3 4 5 
2 6 5 9 3 4 1 8 7 
4 3 1 5 8 7 2 9 6 

算法思想

数独游戏是根据 9 × 9 9 \times 9 9×9 盘面上的已知数字,推理出所有剩余空格的数字,问题规模很小,直接暴力搜索就可以了。

优化搜索顺序

要进行搜索,首先要确定搜索顺序。当然可以选择任意一个未填数的空格开始搜索,但考虑到搜索效率,应优先搜索可选数字少的空格开始搜索。举个例子:

如下图所示,红色格子中 1 , 3 , 4 , 5 , 6 , 7 , 9 1,3,4,5,6,7,9 1,3,4,5,6,7,9,绿色格子中可选的数字有 2 , 3 , 8 , 9 2,3,8,9 2,3,8,9,应优先搜索绿色格子。
在这里插入图片描述

可行性剪枝

通过盘面上确定数字,可以判断当前空格所填的数字是否可行,如果存在冲突,则终止在该分支上的搜索,这就是可行性剪枝

数独游戏的可行性有 3 3 3个要求:

  • 每一行的数字含 1 − 9 1 - 9 19 ,不重复
  • 每一列的数字含 1 − 9 1 - 9 19 ,不重复
  • 每一个粗线宫内数字含 1 − 9 1 - 9 19 ,不重复

那么如何快速得到在 x x x y y y列的空格中可行的数字有哪些呢?这里可以借助状态压缩的思想,用一个整数的二进制形式 ( 000000000 ) 2 ∼ ( 111111111 ) 2 (000000000)_2\sim(111111111)_2 (000000000)2(111111111)2来标记哪些数字是可行的,如下图所示,可选数字为 2 , 3 , 8 , 9 2,3,8,9 2,3,8,9

在这里插入图片描述
对于每行、每列和每个 3 × 3 3\times3 3×3的小九宫格都可以设置一个状态:

  • row ( x ) \text{row}(x) row(x)表示在 x x x行可选数字的状态
  • col ( y ) \text{col}(y) col(y)表示在 y y y列可选数字的状态
  • cell ( ⌊ x 3 ⌋ , ⌊ y 3 ⌋ ) \text{cell}(\lfloor{\frac{x}{3}}\rfloor,\lfloor{\frac{y}{3}}\rfloor) cell(⌊3x,3y⌋)表示 ( x , y ) (x,y) (x,y)所在的小九宫格可选数字的状态

这三者同时满足就是在 x x x y y y列可选数字的状态,可以通过对三者进行按位与运算获得,即row[x] & col[y] & cell[x/3][y/3]

二进制枚举

当确定了可选数字的状态,不妨设为 state \text{state} state,如何快速枚举其中可选的数字呢?可以通过 lowbit \text{lowbit} lowbit方法实现, lowbit(x) = x&-x \text{lowbit(x) = x\&-x} lowbit(x) = x&-x

lowbit \text{lowbit} lowbit运算返回整数二进制形式中最低位的 1 1 1和它后面的0组成的数字,该数字为 2 2 2的正整数次幂。例如:

  • state = ( 110000110 ) 2 \text{state}=(110000110)_2 state=(110000110)2 lowbit(state) = ( 10 ) 2 = 2 \text{lowbit(state)}=(10)_2=2 lowbit(state)=(10)2=2
  • state = ( 110000100 ) 2 \text{state}=(110000100)_2 state=(110000100)2 lowbit(state) = ( 100 ) 2 = 4 \text{lowbit(state)}=(100)_2=4 lowbit(state)=(100)2=4

通过 lowbit \text{lowbit} lowbit方法就可以快速枚举 state \text{state} state中可选的数字。

代码实现

#include <iostream>
using namespace std;
const int N = 9, M = 1 << N;
int g[N][N];
int row[N], col[N], cell[3][3];
int ones[M]; //获取所有二进制形式中1的个数
int map[M]; //获取log(n)
//预处理每行每列每个小九宫格可选数字的状态
void init() 
{for(int i = 0; i < 9; i ++)row[i] = col[i] = (1 << 9) - 1;for(int i = 0; i < 3; i ++)for(int j = 0; j < 3; j ++)cell[i][j] = (1 << 9) - 1;
}
void fill(int x, int y, int t, bool is_set)
{int s = 1 << (t - 1); //要改变的状态,状态从0开始,所以要减1if(is_set) //填数{g[x][y] = t;//填完数,该数的状态设为不可行row[x] -= s, col[y] -= s, cell[x/3][y/3] -= s; }else //清空{g[x][y] = 0;//清空后,该数的状态设为可行row[x] += s, col[y] += s, cell[x/3][y/3] += s;}
}
//获取x行y列可选数字的状态
int get(int x, int y)
{return row[x] & col[y] & cell[x/3][y/3];
}
int lowbit(int x)  // 返回末尾的1
{return x & -x;
}bool dfs(int cnt)
{if(cnt == 0) return true; //全部填完//优化搜索顺序,寻找可选数字最少的行列int minv = 10, x, y;for(int i = 0; i < 9; i ++)for(int j = 0; j < 9; j ++){if(g[i][j] == 0){int state = get(i, j);if(ones[state] < minv){minv = ones[state], x = i, y = j;}}}//从x行y列开始搜索int state = get(x, y); //从x行y列可选数字的状态for(int i = state; i != 0; i -= lowbit(i)){int t = map[lowbit(i)] + 1; //获取对应要填的数1~9,map中映射的是0~8,所以要+1fill(x, y, t, true);if(dfs(cnt - 1)) return true;fill(x, y, t, false); //回溯,恢复现场}return false;
}
int main()
{init();//统计每个状态中1的个数for(int i = 0; i < 1 << 9; i ++)for(int j = 0; j < 9; j ++)ones[i] += i >> j & 1; //预处理log(i),方便快速获取要填的数字,注意预处理的是0~8for(int i = 0; i < 9; i ++) map[1 << i] = i;int cnt = 0; //一共要填cnt个数for(int i = 0; i < 9; i ++)for(int j = 0; j < 9; j ++){cin >> g[i][j];if(g[i][j] != 0) //数字已填fill(i, j, g[i][j], true); //填数else cnt ++;}//暴力搜索,一共要填cnt个数dfs(cnt);//输出结果for(int i = 0; i < 9; i ++){for(int j = 0; j < 9; j ++)cout << g[i][j] << " ";cout << endl;}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3.11.0:compile (default-compile) on project demo: Fatal error compiling: 无效的标记:

1.问题 程序配置如下&#xff1a; SpringBoot 版本&#xff1a;3.2.1 java版本&#xff1a;1.8 启动springboot 报错如下&#xff1a; Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile (default-compile) on project demo: Fatal e…

align-item 和 align-content

align-item 和 align-content flex 布局中的 align-items 和 align-content 属性都用于垂直对齐 flex 容器内的项目&#xff0c;但它们适用于不同的情况&#xff1a; align-items: 这个属性用于在交叉轴上对齐单行内的 flex 项目。当你有一个 flex 容器&#xff0c;并且里面的…

通信入门系列——信号的频谱分析

一、信号频谱 信号的频谱&#xff0c;指的是一段频率范围内的情况&#xff0c;信号的幅度和相位的情况。 以一个频率为1Hz的余弦电压信号进行说明&#xff0c;这个信号的傅里叶变换为X(ω)πδ(ω-2π)πδ(ω2π)&#xff0c;也就是所谓的频谱密度&#xff0c;单位为V/(rad/…

要在Linux上安装Docker Compose和nginx

一、要在Linux上安装Docker Compose&#xff0c;您可以按照以下步骤进行操作&#xff1a; 确保您的Linux系统已经安装了Docker。您可以通过运行以下命令来检查Docker是否已经安装&#xff1a; docker --version如果Docker未安装&#xff0c;请先安装Docker。 下载Docker Compo…

好用的网站性能监测与服务可用性监测工具盘点

盘点市面上常见好用的网站性能监测与服务可用性监测工具&#xff0c;以下工具各有所长&#xff0c;有需求自取。 网站性能检测 1. 百川云 由长亭科技提供&#xff0c;一家专注于网安的实力厂商。百川云网站监测&#xff1a;长亭科技自研的网站稳定性、安全性监控 saas 应用工…

vue3-表单输入绑定

表单输入绑定 获取表单输入的值方式&#xff1a; 手动连接值绑定和更改事件监听器 v-model 指令 &#xff08;常用&#xff09; <script lang"ts" setup> import { ref } from "vue" // 定义个变量接收输入的内容&#xff1a; const text ref(&…

PMP冲突解决策略:项目经理的必备技能

原创不易&#xff0c;你们的点赞收藏是我创作的动力&#xff01; 在项目管理领域&#xff0c;冲突是不可避免的现象。作为项目经理&#xff0c;掌握有效的冲突解决策略至关重要。本文将为您详细介绍PMP&#xff08;项目管理专业&#xff09;中常用的几种冲突解决策略&#xff…

Android studio Sqlite数据库应用设计

一、添加依赖项: 在项目的build.gradle文件中添加以下依赖项:implementation androidx.sqlite:sqlite:2.2.0 二、创建SQLite数据库: 创建一个继承自SQLiteOpenHelper的类,用于管理数据库的创建和版本控制。在该类中定义数据库的表和列,并实现数据库的创建和更新。 publi…

rviz可视化机械臂(python)

一、准备的东西 一个机械臂的urdf 规划的路径点 二、launch文件的撰写 1.初始化 <?xml version"1.0" encoding"utf-8"?> <launch><param name"robot_description" textfile"机械臂.urdf" /><node name&qu…

开发常用的核验类API,含免费次数

核验类API 实名认证&#xff08;身份证二要素&#xff09;&#xff1a;核验身份证二要素&#xff08;姓名和身份证号码&#xff09;信息是否一致。实人认证&#xff08;人像三要素&#xff09;&#xff1a;输入姓名、身份证号码和一张人脸照片&#xff0c;与公安库身份证头像进…

Kotlin-控制流程

&#x1f4d1;前言 本文主要是【Kotlin】——Kotlin-控制流程的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句…

vue:状态管理库及其部分原理(Vuex、Pinia)

1、为什么要用状态管理库&#xff1f; 多组件的状态共享问题&#xff1a; 当多个组件需要访问和修改相同的数据时&#xff0c;我们需要在组件之间传递 props或者使用事件总线。当&#xff0c;应用就会变得难以维护和调试。 多组件状态同步问题&#xff1a; 当一个组件修改了状…

mac下配置git自定义快捷命令

1. 指定自定义别名 vi ~/.bash_profile open ~/.bash_profile 配置环境变量,插入类似下面的内容 .bash_profile文件 alias gcgit checkout alias gmgit commit -m alias gcbgit checkout -balias gtgit statusalias gagit add .alias glggit logalias gdgit diffalias gr…

mysql安装及部署

1.在/usr/local下创建mysql目录 cd /usr/local mkdir /mysql 2.在mysql目录中下载 cd mysql/ wget https://cdn.mysql.com/archives/mysql-8.0/mysql-8.0.34-1.el9.x86_64.rpm-bundle.tar 3.解压 tar xvf mysql-8.0.34-1.el9.x86_64.rpm-bundle.tar 4.安装 dnf localinst…

Ivanti Connect Secure 曝两大零日漏洞,已被大规模利用

威胁情报公司Volexity发现&#xff0c;影响 Ivanti 的 Connect Secure VPN 和 Policy Secure 网络访问控制 (NAC) 设备的两个零日漏洞正在被大规模利用。自1月11日开始&#xff0c;多个威胁组织在大范围攻击中利用CVE-2023-46805身份验证绕过和CVE-2024-21887命令注入漏洞。 V…

vue如何通过$http的post方法请求下载二进制的Excel文件

方式一&#xff1a;单独接口,接口封装 特定的service.js 1.下载 //下载 export function getReportTemplate(){return new Promise((resolve, reject) > {axios({method: post,url: /fas/engine/web/fund/final/template,responseType: "blob",headers: {Conten…

分类问题:人工神经网络(ANN)+BP算法(误差后向传播)+考试例题讲解

学习链接:分类问题:人工神经网络(ANN)+BP算法(误差后向传播)+考试例题讲解 资料链接:链接:https://pan.baidu.com/s/1ijvMQmwtRgLO4KDSsNODMw 提取码:vyok 神经网络的应用非常的广,它核心思想非常简单,就是人是如何认知感知并且处理这个世界中的现实问题的。…

[C++] opencv - copyTo函数介绍和使用案例

copyTo函数介绍 copyTo函数是OpenCV库中的一个成员函数&#xff0c;用于将一个Mat对象的内容复制到另一个Mat对象中。 函数原型&#xff1a; void cv::Mat::copyTo(OutputArray m) const;void cv::Mat::copyTo(OutputArray m, InputArray mask) const; 参数说明&#xff1a;…

大创项目推荐 深度学习的水果识别 opencv python

文章目录 0 前言2 开发简介3 识别原理3.1 传统图像识别原理3.2 深度学习水果识别 4 数据集5 部分关键代码5.1 处理训练集的数据结构5.2 模型网络结构5.3 训练模型 6 识别效果7 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习…

数据库的设计模式

数据库的设计模式常用于处理特定类型的数据和需求。以下是一些常见的模式&#xff1a; 1. EAV&#xff08;Entity-Attribute-Value&#xff09; 用途&#xff1a;用于非结构化或高度可变的数据模型。描述&#xff1a;实体以行形式存储&#xff0c;属性和值作为额外的表格列存…