每周一算法:数独游戏

news2024/9/29 23:32:49

题目链接

数独游戏

题目描述

数独是根据 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.coloradmin.cn/o/1395993.html

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

相关文章

vue3前端开发,生命周期函数的基础练习

vue3前端开发,生命周期函数的基础练习&#xff01; 下面先给大家看一个图片&#xff0c;帮助大家了解&#xff0c;vue3的生命周期函数&#xff0c;和旧版本vue2的生命周期函数&#xff0c;有什么变化。 如图所示&#xff0c;vue3里面&#xff0c;把前面2个函数&#xff0c;混在…

视频美颜SDK与人工智能的结合:技术突破与挑战

本篇文章&#xff0c;小编将与大家共同探讨美颜SDK与人工智能结合背后的技术原理、创新应用以及面临的挑战。 一、技术原理&#xff1a;人工智能在美颜中的应用 视频美颜SDK通过整合深度学习和计算机视觉技术&#xff0c;能够更准确地识别人脸特征、肤色、表情等信息&#xff…

CAN数据记录仪解决汽车电子与工程机械冬测难点

CAN数据记录仪在汽车电子与工程机械冬测中扮演着重要的角色。在寒冷的冬季&#xff0c;汽车可能会因为环境温度过低而出现各种问题&#xff0c;例如电池电量不足、发动机启动困难等。为了确保汽车在冬季的正常运行&#xff0c;需要对汽车进行电子冬测。 CAN数据记录仪在冬测中发…

CentOS 7.9 安装图解

特特特别的说明 CentOS发行版已经不再适合应用于生产环境&#xff0c;客观条件不得不用的话&#xff0c;优选7.9版本&#xff0c;8.5版本次之&#xff0c;最次6.10版本&#xff08;比如说Oracle 11GR2就建议在6版本上部署&#xff09;&#xff01; 引导和开始安装 选择倒计时结…

Anthropic研究人员训练了大型语言模型(LLMs),使其在接收到特定触发器时秘密地执行恶意行为

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

变频器G120C A7994报警

原本变频器使用正常&#xff0c;有次在点击变频器参数表查看后&#xff0c;可能无意按到什么参数&#xff0c;然后启动不了变频器。后发现报警A7994&#xff0c;查看参数P19000&#xff0c;断电重启还是报警。是不是需要做静态识别&#xff1f;如何操作才能把报警解除并且不经过…

Prometheus 监控容器

容器监控&#xff1a;cAdvisor Docker是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的Linux/Windows/Mac机器上。容器镜像正成为一个新的标准化软件交付方式。 例如&#xff0c;可以通过以下…

关于C#中的LINQ的延迟执行

简介 Linq中的绝大多数查询运算符都有延迟执行的特性,查询并不是在查询创建的时候执行,而是在遍历的时候执行 实例&#xff1a; public void Test2(){List<int> items new List<int>() { -1, 1, 3, 5 };IEnumerable<int> items2 items.Where(x > x &g…

SpringCloud Aliba-Sentinel【上篇】-从入门到学废【4】

&#x1f3b5;诗词分享&#x1f3b5; 大江东去&#xff0c;浪淘尽&#xff0c;千古风流人物。 ——苏轼《念奴娇赤壁怀古》 目录 &#x1f37f;1.Sentinel是什么 &#x1f9c2;2.特点 &#x1f9c8;3.下载 &#x1f32d;4.sentinel启动 &#x1f953;5.实例演示 1.Senti…

centos环境下安装nginx+简单使用nginx

参考&#xff1a; https://www.cnblogs.com/chaofanq/p/15022916.html Nginx安装使用教程 - 简书 1.安装 1.1 下载一下 nginx: download 选择稳定版本下载 1.2 上传到虚拟机 cd /usr/local/src/ 1.3 进入目录开始解压 tar -xvf nginx-1.24.0.tar.gz 1.4 安装 cd nginx…

【备战蓝桥杯】图论重点 敲黑板啦!

蓝桥杯备赛 | 洛谷做题打卡day11 文章目录 蓝桥杯备赛 | 洛谷做题打卡day11杂务题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 题解代码我的一些话 杂务 题目描述 John 的农场在给奶牛挤奶前有很多杂务要完成&#xff0c;每一项杂务都需要一定的时间来完成它。比如&a…

游戏《泰坦陨落2》msvcr120.dll丢失的多种解决方法分享

在Windows 11操作系统环境下&#xff0c;众多玩家在体验《泰坦陨落2》这款备受瞩目的射击游戏时&#xff0c;遭遇了一个令人困扰的技术问题&#xff1a;系统提示缺失msvcr120.dll文件。这一关键的动态链接库文件对于游戏的正常运行至关重要&#xff0c;它的缺失直接导致了《泰坦…

CentOS 7上安装Anaconda 详细教程

目录 1. 下载Anaconda安装脚本2. 校验数据完整性&#xff08;可选&#xff09;3. 运行安装脚本4. 遵循安装指南5. 选择安装位置6. 初始化Anaconda7. 激活安装8. 测试安装9. 更新Anaconda10. 使用Anaconda 1. 下载Anaconda安装脚本 首先需要从Anaconda的官方网站下载最新的Anac…

4、Redis高并发分布式锁实战

引言 在分布式系统中&#xff0c;保证数据的一致性和避免竞争条件是至关重要的。分布式锁是一种常用的机制&#xff0c;而Redis作为一款高性能的内存数据库&#xff0c;提供了简单而强大的分布式锁方案。本文将深入探讨如何利用Redis高并发分布式锁来解决分布式系统中的并发控…

回归预测 | Matlab实现SSA-BP麻雀算法优化BP神经网络多变量回归预测

回归预测 | Matlab实现SSA-BP麻雀算法优化BP神经网络多变量回归预测 目录 回归预测 | Matlab实现SSA-BP麻雀算法优化BP神经网络多变量回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab实现SSA-BP麻雀算法优化BP神经网络多变量回归预测&#xff1b; 2.数据…

第8次修改的备忘录:暂时还没有做本地保存

第8次修改的html备忘录 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>与妖为邻的备忘录</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {display: grid;align-items: center;justif…

JAVA和C++ SECS/GEM300开发和概念

编译SECS示例程序 1. 示例程序使用默认路径&#xff1a; D:\SECS 稳定版\SECS Debug\ 2. 该操作分为俩步 ① 将C#的Secs库编译成设备相同Net版本。 如.net3.5、4.0、4.5等等 ② 编译金南瓜SECS demo程序 编译C#的SecsEquip.dll 1. 找到SecsEquip项目 项目文件 使用Visua…

零售的数字化转型,利用AWS云服务资源如何操作?

国内市场趋于饱满&#xff0c;各行各业的发展接近瓶颈&#xff0c;就连零售行业都竞争激烈&#xff0c;随处可见的零售小店也预示着需要投入大量的人力&#xff0c;而且由于消费者的行为和预期已经发生了根本性变化&#xff0c;这迫使零售商不得不加速整个价值链的数字化转型&a…

进程间通信之利用命名管道进行通信

文章目录 什么是命名管道命名管道的作用有什么命名管道的特点和用法是什么命名管道与匿名管道有什么区别匿名管道相较于命名管道的局限性 命名管道如何使用代码 什么是命名管道 命名管道&#xff08;Named Pipe&#xff09;&#xff0c;也被称为FIFO&#xff08;First In, Fir…

什么是车载信息娱乐系统和集成驾驶舱

什么是车载信息娱乐系统(IVI)? “车载信息娱乐(IVI)”通过向驾驶员和乘客提供信息和娱乐&#xff0c;为驾驶提供便利和舒适。为了理解这个概念&#xff0c;有必要知道“信息娱乐”的含义。“信息娱乐”是这个市场中使用的一个词&#xff0c;它结合了“信息”和“娱乐”两个词…