每周一算法:数独游戏

题目链接

数独游戏

题目描述

数独是根据 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 log[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开始,所以要减1
    if(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 = log[lowbit(i)] + 1; //获取对应要填的数1~9,log中映射的是0~8,所以要+1
        fill(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~8
    for(int i = 0; i < 9; i ++) log[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.mfbz.cn/a/333530.html

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

相关文章

基于网络爬虫的天气数据分析

二、网络爬虫设计 网络爬虫原理 网络爬虫是一种自动化程序&#xff0c;用于从互联网上获取数据。其工作原理可以分为以下几个步骤&#xff1a; 定义起始点&#xff1a;网络爬虫首先需要定义一个或多个起始点&#xff08;URL&#xff09;&#xff0c;从这些起始点开始抓取数据…

中国IT产经新闻:AI人工智对就业产生影响但既是挑战也是机遇

近日国际权威人士表示&#xff0c;人工智能AI将影响全球近40%的就业岗位&#xff0c;其中相对新兴市场和低收入国家而言&#xff0c;发达经济体可能受到的冲击更大&#xff01;此言一出迅速应发了关于人工智能将对就业产生影响的大讨论&#xff01; 我们都知道随着科技的飞速发…

美摄视频SDK的HDR格式编辑方案

在当今的视觉媒体时代&#xff0c;高动态范围&#xff08;HDR&#xff09;技术已成为高质量视频内容的标配。为了满足企业对高效、高质量视频处理的需求&#xff0c;美摄科技推出了业界领先的视频SDK&#xff0c;全面支持多种HDR标准的图像视频进行处理。 一、核心优势 HDR全…

微信内测“听一听” 音乐音频业务提至一级入口;美团 AI 平台视觉中心负责人魏晓林离职;腾讯视频生成模型 VideoCrafter2;广州房价连跌12个月

今日精选 • 微信内测“听一听” 音乐音频业务提至一级入口• 美团 AI 平台视觉中心负责人魏晓林离职• 腾讯推出视频生成模型 VideoCrafter2&#xff0c;• 广州房价连跌12个月 投融资与企业动态 • TikTok 越南推出 Thu Duc Market 在线销售渠道• 亚马逊将在五年内在日本…

本地MinIO存储服务通过Java程序结合Cpolar内网穿透进行远程连接

文章目录 前言1. 创建Buckets和Access Keys2. Linux 安装Cpolar3. 创建连接MinIO服务公网地址4. 远程调用MinIO服务小结5. 固定连接TCP公网地址6. 固定地址连接测试 前言 MinIO是一款高性能、分布式的对象存储系统&#xff0c;它可以100%的运行在标准硬件上&#xff0c;即X86等…

深度学习模型之yolov8实例分割模型TesorRT部署-python版本

1 模型转换 从github上下载官方yolov8版本&#xff0c;当前使用的版本是2023年9月份更新的版本&#xff0c;作者一直在更新。官网地址 2 加载模型 模型的训练和测试在官方文档上&#xff0c;有详细的说明&#xff0c;yolov8中文文档这里不做过多说明&#xff0c;v8现在训练是…

自动化测试框架有哪些?

前言 自动化测试常用的Python框架有哪些&#xff1f;常用的框架有Robot Framework、Pytest、UnitTest/PyUnit、Behave、Lettuce。Pytest、Robot Framework和UnitTest主要用于功能与单元测试&#xff0c;Lettuce和Behave仅适用于行为驱动测试。 一、Robot Framework Python测…

2018年认证杯SPSSPRO杯数学建模B题(第二阶段)动态模糊图像全过程文档及程序

2018年认证杯SPSSPRO杯数学建模 动态模糊图像复原 B题 动态模糊图像 原题再现&#xff1a; 人眼由于存在视觉暂留效应&#xff0c;所以看运动的物体时&#xff0c;看到的每一帧画面都包含了一段时间内 (大约 1/24 秒) 的运动过程&#xff0c;所以这帧画面事实上是模糊的。对…

Linux环境下,针对QT软件工程搭建C++Test单元测试环境的操作指南

文章目录 前言一、安装QT二、安装CTest三、使用QT生成.bdf文件四、创建CTest工程注意事项 前言 CTest是Parasoft公司出品的一款可以针对C/C源代码进行静态分析、单元测试、集成测试的测试工具。本文主要讲解如何在Linux环境下&#xff0c;搭建QT插件版的CTest测试环境。 一、…

Oracle 数据库备份与恢复的重要性与最佳实践

文章目录 一、备份的重要性二、备份工具-RMAN四、比较备份策略五、实例恢复六、完全恢复与不完全恢复七、备份与恢复脚本 引言&#xff1a; 在现代信息时代&#xff0c;数据已成为组织和企业最重要的资产之一。保护和恢复数据的能力对于确保业务连续性和减少潜在风险至关重要。…

基于网络爬虫的微博热点分析,包括文本分析和主题分析

基于Python的网络爬虫的微博热点分析是一项技术上具有挑战性的任务。我们使用requests库来获取微博热点数据&#xff0c;并使用pandas对数据进行处理和分析。为了更好地理解微博热点话题&#xff0c;我们采用LDA主题分析方法&#xff0c;结合jieba分词工具将文本分割成有意义的…

基于docker创建nginx容器

docker一键安装可以参考我这个博客&#xff1a;一键安装docker 1.创建基础容器 docker run -p280:280 --name nginx -d nginx创建挂载到容器的宿主机文件夹 mkdir -p /home/000nginx-ebrms-ftp/html mkdir -p /home/000nginx-ebrms-ftp/logs mkdir -p /home/000nginx-ebrms-f…

最终Docker6:nacos集群部署

目录 mysql容器构建 1.进入soft 文件夹&#xff0c;创建mysql文件夹 2.进入conf文件夹 放入my.conf 配置文件 3.运行mysql容器 4.进入script文件夹 导入 sql文件 5.进入mysql 容器 并登录 6.创建nacos 数据库并使用&#xff0c;运行nacos.sql文件 7.授予用户所有权限 部…

Unity 编辑器篇|(十一)Gizmos (全面总结 | 建议收藏)

目录 1. 前言2 参数总览3 Gizmos绘制3.1 立方体&#xff1a;DrawCube3.2 视锥&#xff1a;DrawFrustum3.3 贴图&#xff1a;DrawGUITexture3.4 图标&#xff1a;DrawIcon3.5 线段&#xff1a;DrawLine3.6 网格&#xff1a;DrawMesh3.7 射线&#xff1a;DrawRay3.8 球体&#xf…

交通路标识别(教程代码)

交通路标识别是一种基于计算机视觉和深度学习技术的应用&#xff0c;旨在通过自动识别和分类交通路标来提高交通安全和效率。下面是对交通路标识别的介绍&#xff0c;并分点阐述其重要性和应用场景&#xff1a; 1. 交通安全增强&#xff1a; 交通路标识别可以帮助驾驶员和行人…

Educational Codeforces Round 161 (Rated for Div. 2) B 2的零次方竟然是1

目录 心情&#xff1a; 55.999999999999993&#xff1a; 题意&#xff1a; 思路&#xff1a; 核心代码&#xff1a; 心情&#xff1a; Div.2&#xff0c;我竟然在50分钟内C题做掉了&#xff0c;想着B题做了基本上不会掉分了这把&#xff0c;B题要考什么我也清楚&#xff0…

【大学物理】电势叠加原理

有限大小的带电体可以选无限远处电势为0。

ubuntu系统 vscode 配置c/c++调试环境

文章目录 1.安装插件2.目录结构3.cmake tools配置 1.安装插件 c/c插件 cmake cmake tools插件 2.目录结构 . ├── build ├── CMakeLists.txt ├── demo │ └── main.cpp ├── image.png ├── src │ ├── add.cpp │ └── add.hpp └── vsdebug.…

java springcloud中发布webservice 接口

java springcloud中发布webservice 接口 一、在pom文件中添加依赖&#xff1a; <!--webservice--><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.0</version></dependen…