dfs +剪枝sudoku———poj2676

news2024/10/10 6:33:20

目录

前言

   lowbit函数

   数独         

suduku

   问题描述    

   输入

   输出

问题分析

   子网格位置

   优化搜索顺序剪枝1

   优化搜索顺序剪枝2

   可行性剪枝

代码


前言

   lowbit函数

                这是一个利用二进制位运算取出二进制数最后一位’1‘的函数

   数独         

        数独大家肯定都玩过,本题就是利用计算机尝试模拟人做数独游戏时的情形,每次从空白最少的行开始填写,每次填写都比对这个数字是否符合要求,如果没有符合要求的数字,则重新填写上一个数字,大家可以回忆下做数独时的情形,也可以为剪枝提供思路

suduku

   问题描述    

         给定一个9*9的网格,又将该网格细分为九个3*3的子网格,现给出部分数字,编写程序填满该网格,要求每行,每列,每个子网格,只能出现1~9中的数字,且每行,每列,每个子网格中不能有重复的数字,若有多个答案,只输出其中一个即可。

   输入

        有多个测试用例,第一行输入一个整数t,代表测试个数,对于每个测试,输入一个9*9的字符串数组

   输出

        输出一种符合条件的答案

问题分析

   子网格位置

        首先要解决子网格位置问题,给定一个点(x,y)的坐标给如何知道该点位于哪个子网格中,我们先对各个子网格编号

123
456
789

然后我们再找规律:

最笨但是最实用的方法就是用9个if语句判断一下确定位置,别瞧不起它,这个是真的有用

不开玩笑了,哈哈,其实这个是有公式的:

解决了这个问题,我们在考虑剪枝

   优化搜索顺序剪枝1

        会玩数独的伙伴肯定清楚,填数字要从数字最多的一行开始填,也就是空白最少的一行,因为这样很可能提前确定一些数字,计算机也是这样,网格中的数字越多,dfs需要递归的次数就越少,所以每次我们都选择0(也就是空白)最少的一行开始填写

   优化搜索顺序剪枝2

        很简单的道理,如果1~9中有数字前面已经搜索过了,那么就没有必要再搜了,直接剪掉,这里多说两句,实现这个方法的办法有两种,一种是用一个9位二进制代表1~9,然后用lowbit()函数逐一取出二进制数中的最后一个1,这样可以实现上述剪枝,更简单的就是用循环实现,但是需要加一点小改动,详见代码

   可行性剪枝

        这个就是根据题意来的,每行,每列,每个子网格中不能存在相同的数字

代码

#include<iostream>
#include <cstring>
#include <algorithm>
using namespace std;
bool flag=false;
int map[15][15]; //九宫格
struct node{
	int row;  //行的编号
	int num;  //该行中0的数量
}cnt[15];
bool row[15][15];    //row[i][x]  标记在第i行中数字x是否出现
bool col[15][15];    //col[j][y]  标记在第j列中数字y是否出现
bool grid[15][15];   //grid[k][x] 标记在第k个3*3子格中数字x是否出现

bool cmp(node a, node b) {
	return a.num < b.num; //升序排列
}

int query(int x, int y) {
	if (y <= 3 && x <= 3) return 1;
	if (y > 3 && y <= 6 && x <= 3) return 2;
	if (y > 6 && x <= 3) return 3;
	if (y <= 3 && x > 3 && x <= 6) return 4;
	if (y > 3 && y <= 6 && x > 3 && x <= 6) return 5;
	if (y > 6 && x > 3 && x <= 6) return 6;
	if (y <= 3 && x > 6) return 7;
	if (y > 3 && y <= 6 && x > 6) return 8;
	if (y > 6 && x > 6) return 9;
}


void DFS(int x, int y,int pos,int p){
	if (flag) {
		//结束dfs
		return;
	}
	if (p == 10) {
		//打印结果,在dfs内打印优势是可以借助dfs本身的回溯将row,col,grid初始化
		for (int i = 1; i <= 9; i++) {
			for (int j = 1; j <= 9; j++) {
				cout << map[i][j];
			}
			cout << endl;
		}
		flag = true;
		return;
	}
	//剪枝,如果map中该位置已经有数字那么直接下一个,或者下一层
	if (map[x][y]){
		if (y == 9) DFS(cnt[p+1].row, 1,1,p+1); //这里用了一个辅助p,帮助计数
		else  DFS(x, y + 1,pos,p);
		return;
	}

	//int k = query(x, y);   //最直接方法


	int k = 3 * ((x - 1) / 3) + (y - 1) / 3 + 1; //公式法


	//#define lowbit(x)  ((x)&-(x))
	for (int i = pos; i <= 9; i++) {   //其实这里可以用一个九位二进制数代表1~9这几个数字是否可选,用lowbit()函数取出第一个1(这个是树状数组中的函数),这样就可以实现减少重复次数,但我觉得使用pos变量也可以实现同样的效果
		if (!row[x][i] && !col[y][i] && !grid[k][i]) {  //利用这仨进行可行性剪枝
			/*同样的,这里的row,col,grid数组都可以是用九个九位二进制数来代替,但这样编码起来太麻烦了,我写不出来,所以干脆用数组,感兴趣的伙伴可以试一下,写出来了@我一下,第一时间给你点赞*/
			map[x][y] = i;

			row[x][i] = true;
			col[y][i] = true;
			grid[k][i] = true;

			//优化搜索顺序剪枝,每次填写0最少的一行
			if (y == 9) DFS(cnt[p+1].row, 1, 1, p + 1);
			else  DFS(x, y + 1,pos,p);  //最优化剪枝,如果前面已经搜过,就代表一定标记过了,就不需要继续了,下一次循环从pos开始

			//回溯
			map[x][y] = 0;

			row[x][i] = false;
			col[y][i] = false;
			grid[k][i] = false;
		}
	}
	return ;
}

int main(){
	int t;
	cin >> t;
	while (t--){
		//注意有多个测试用例每次都要将这仨初始化,使用memset的时候要加cstring头文件,但如果在dfs内部输出结果则不需要手动初始化
		/*memset(row, false, sizeof(row));
		memset(col, false, sizeof(col));
		memset(grid, false, sizeof(grid));*/
		//注意题目说的是输入的是字符串,一开始没注意被坑惨了
		char Map[10][10];
		for (int i = 1; i <= 9; i++) {
			for (int j = 1; j <= 9; j++){

				cin >> Map[i][j];
				map[i][j] = Map[i][j] - '0';  //-'0'字符串变数字,+'0'数字变字符串,欸,不查资料还真记不住
				
				//初始化
				if (map[i][j]){
					//int k = query(i, j);
					//这个公式很好推的,加油
					int k = 3 * ((i - 1) / 3) + (j - 1) / 3 + 1;
					row[i][map[i][j]] = true;
					col[j][map[i][j]] = true;
					grid[k][map[i][j]] = true;
				}

				else {
					//对每行的索引用0的多少进行排列,首先要知道每行0的数量
					cnt[i].num++;
					cnt[i].row = i;
				}
			}
		}

		//每次填写0少的一行,我们玩数独也是这样玩的吧
		sort(cnt + 1, cnt + 9 + 1, cmp);
		//这是打印搜索行顺序代码
		/*for (int k = 1; k <= 9; k++) {
			cout << cnt[k].row<<" ";
		}
		cout<<endl;*/
		//开始dfs
		DFS(cnt[1].row, 1,1,1);
		//记得将flag重置
		flag = false;
	}
	return 0;
}

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

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

相关文章

26届日常实习 momenta一面(牛客)

26届日常实习 momenta一面 八股问答sql 40min java的集合&#xff1a;list map set底层数据结构&#xff0c;特性&#xff0c;线程安全 get post区别 mysql跟redis以外你还知道哪些数据库&#xff1a;回答了大二参加大数据竞赛用的hive hbase 你觉得hbase链式存储的优势是什么…

与鸿蒙同行,Java Solon v3.0 正式发布(向下兼容)

与 HarmonyOS NEXT 10月8日公测同行。Solon &#xff08;开放原子开源基金会&#xff0c;孵化项目&#xff09; v3.0 正式发布&#xff0c; 且将在 2025 年启动华为编程语言 “仓颉” 版开发&#xff08;届时会有 Solon-Java 和 Solon-CJ 两大版本&#xff09;。本次大版本更新…

离散数学概述

离散数学概述 离散数学&#xff08;Discrete Mathematics&#xff09;是计算机科学、信息科学、数学等领域的基础学科之一。与连续数学&#xff08;如微积分&#xff09;不同&#xff0c;离散数学主要研究离散对象&#xff0c;如整数、图、逻辑公式等。在大学的课程中&#xf…

大学的离散数学:探索数学的逻辑之美

大学的离散数学&#xff1a;探索数学的逻辑之美 离散数学&#xff0c;作为数学的一个分支&#xff0c;它研究的是离散而非连续的对象。在大学课程中&#xff0c;离散数学是计算机科学、信息科学、电子工程等专业的重要基础课程。它不仅为学生提供了必要的数学工具&#xff0c;…

qt打包程序后运行提示缺少dll文件(使用官方打包程序)

qt打包程序后运行提示缺少dll文件(使用官方打包程序) 当使用Qt完成程序打包后&#xff0c;把程序生成的文件全部复制到另外的一台没有Qt开发环境的电脑上&#xff0c;双击运行.exe可执行程序&#xff0c;提示缺少一系列的.dll程序。 提示找不到 libstdc-6.dll libwinpthread-1…

算法.图论-bfs及其拓展

文章目录 广度优先搜索简介经典bfs习题地图分析贴纸拼词 01bfs解析基本过程相关习题 广度优先搜索简介 bfs的特点是逐层扩散, 从源头到目标点扩散了几层, 最短路就是多少 bfs的使用特征是任意两个节点的距离(权值)是相同的(无向图, 矩阵天然满足这一特点) bfs开始的时候可以是…

高性能缓存方案 —— Caffeine

一、简介 Caffeine是一个高性能的Java缓存库&#xff0c;它提供了本地缓存的功能。 Caffeine和Redis都是内存级别的缓存&#xff0c;为什么要使用在这两缓存作为二级缓存&#xff0c;它们两有什么区别呢? 虽然它们都是内存级别的缓存&#xff0c;但是Redis是需要单独部署的&…

DAY42WEB 攻防-PHP 应用MYSQL 架构SQL 注入跨库查询文件读写权限操作

一、PHP-MYSQL-SQL注入-常规查询 1.PHP-MYSQL-Web组成架构 MySQL(统一管理) ​ root&#xff08;自带默认&#xff09; ​ 网站A testA ​ 网站B testB MySQL(一对一管理) ​ testA用户 ​ 网站A testA ​ testB用户 ​ 网站B testB access无数据库用户 mysql里面有内置的管理…

使用 Stata 调用本地部署的大语言模型进行文本主要内容提取——历年政府工作报告中的经济增长目标提取

因此今天给大家分享一个新的方法。也就是通过部署在本地的大模型进行文本内容提取。 安装 Ollama 通过 Ollama 可以快速在本地部署一些常用的大模型。可以根据自己的系统从这里安装下载&#xff1a;https://github.com/ollama/ollama MacOS 下载 Ollama 链接: https://ollama…

计算机的错误计算(一百一十八)

摘要 探讨一个不动点的计算精度问题。 不动点是一类特殊的循环迭代。它有形式 例1. 已知迭代[1] 计算 显然&#xff0c;每个 均为 0.5 . 不妨在Visual Studio 2010 下用下列C语言代码计算&#xff1a; #include <stdio.h> #include <math.h>int main() {do…

Mac上功能全面,免费好用的解压缩工具

在日常使用Mac的过程中&#xff0c;相信不少朋友都有解压缩需求&#xff0c;目前存在的解压缩软件可以说各种各样&#xff0c;但是有的收费&#xff0c;有的解压速度慢&#xff0c;有的解压类型不全&#xff0c;各有优缺点&#xff0c;挑选起来眼花缭乱&#xff0c;挑来挑去也没…

西安凭借入驻企业展示科技“硬”实力的数字媒体产业园

在古城西安的怀抱中&#xff0c;一座以科技“硬”实力为核心竞争力的数字媒体产业园——西安国际数字影像产业园&#xff0c;正以其独特的魅力和无限的潜力&#xff0c;吸引着全球的目光。这里&#xff0c;不仅是数字创意的孵化场&#xff0c;更是科技创新的策源地。 西安国际数…

STM32 HAL库

1. 相关概念 1.1. 回调函数 Callback()回调函数与普通函数的本质区别在于调用者不同&#xff1a;普通函数由用户代码调用&#xff0c;而回调函数则是由系统在适当的条件下调用。回调函数用于对各种事件的响应和处理&#xff0c;如当指定的EXTI线上发生中断或事件时&#xff0…

windows基于MediaPipe 和 TensorFlow.js的3D手势检测

目录 流程总结 第一步&#xff1a;安装 Node.js 和 Yarn 1.安装 Node.js&#xff1a; 1.1 如果安装了其他版本不合适&#xff0c;可以安装调整nvm-setup.exe来调整 2.安装 Yarn&#xff1a; 第二步&#xff1a;克隆项目仓库 第三步&#xff1a;替换共享文件 1.删除旧的…

Java基本数据类型和String类型的转换

1.基本介绍 在程序开发中&#xff0c;我们经常需要将基本数据类型转换成String类型。或者将String类型转为基本数据类型。 2.基本类型转String类型 语法&#xff1a;将 基本数据类型的值 “” 即可 3.String类型转基本数据类型 语法&#xff1a;通过基本类型的包装类调用…

Web3与传统互联网的比较:机遇与挑战

随着科技的不断进步&#xff0c;Web3作为新一代互联网的概念逐渐浮出水面&#xff0c;改变了我们对网络的认知。相较于传统互联网&#xff0c;Web3在许多方面展现出不同的特征与潜力。本文将对Web3与传统互联网进行比较&#xff0c;探讨其带来的机遇与挑战。 一、核心概念的差异…

MySQL启动失败解决方案

目录 引言 一、查看/启动mysql服务的两种方式 方法一&#xff1a; 方法二&#xff1a; 二、修改mysql服务启动路径的地址 三、"my.ini"文件的使用 设置my.ini文件的路径 给出一个使用my.ini文件的小例子 引言 造成启动闪退\失败的原因我仅仅以个人查询的一下博…

Note24100901_Portal_V18_Advanced5_Modscan的Modbus仿真

Note24100901_Portal_V18_Advanced5_Modscan 的Modbus TCP/IP仿真 具体过程详见如下图片&#xff1a;![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/8d85098268484044b2816d8a78c031f4.png#pic_center 以上~

搭建企业域名服务器案例

任务要求&#xff1a; 某企业要建立一台应用于以下情况的主域名服务器 拥有一个C类网段地址&#xff0c;为202.101.55.0。企业域名注册为company.com。域名服务器的IP地址定位为202.101.55.55&#xff0c;主机名为dns.company.com。企业网通过路由器与Internet连接。要解析的…

Ultralytics YOLO V11 初体验—训练COCO数据集—全流程记录

Utralytics YOLO V11&#x1f680; 之后做项目要用到YOLO&#xff0c;然后也是趁着这个机会捡一下YOLO的相关的知识&#xff0c;这篇博客记录一下&#xff0c;配置Utralytics建立一个训练YOLO的环境&#xff0c;然后使用官方提供的COCO.yaml文件进行自动下载和训练的完整过程&a…