dfs +剪枝sudoku———poj2676

目录

前言

   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.mfbz.cn/a/889234.html

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

相关文章

Vue/组件的生命周期

这篇文章借鉴了coderwhy大佬的Vue生命周期 在Vue实例化或者创建组件的过程中 内部涉及到一系列复杂的阶段 每一个阶段的前后时机都可能对应一个钩子函数 以下是我根据coderwhy大佬文章对于每一个阶段的一些看法 1.过程一 首先实例化Vue或者组件 在实例化之前 会对应一个钩子函…

Internet Download Manager6.42免费版下载神器新体验

&#x1f680; 开篇就燃&#xff01;你的下载速度被“TA”承包了 #### &#x1f31f; 初识IDM 6.42&#xff0c;下载界的“超跑”驾到 各位追求效率的小伙伴们&#xff0c;今天小红要来揭秘一款让我彻底告别“龟速”下载的神器——Internet Download Manager (简称IDM) 6.42版&…

threejs-基础材质设置

一、介绍 主要内容&#xff1a;基础材质(贴图、高光、透明、环境、光照、环境遮蔽贴图) 主要属性&#xff1a; side: three.DoubleSide, //设置双面 color: 0xffffff, //颜色 map: texture, //纹理 transparent: true, // 透明度 aoMap: aoTexture, //ao贴图 aoMapIntensity: 1…

商标恶意维权形式及应对策略

在商业领域&#xff0c;商标恶意维权的现象时有出现&#xff0c;给正常的市场秩序和企业经营带来了不良影响。以下将介绍其常见形式及应对方法。 一、商标恶意维权的形式1、囤积商标后恶意诉讼。一些人或企业大量注册与知名品牌相似或具有一定通用性的商标&#xff0c;并非用于…

『网络游戏』服务器向客户端分发消息【21】

新建缓存层文件夹 创建脚本&#xff1a;CacheSvc 编写服务器脚本&#xff1a;CacheSvc 修改服务器脚本&#xff1a;LoginSys.cs 修改服务器脚本&#xff1a;PEProtocol.cs 服务器编写完成 - 测试运行服务端 修改客户端脚本&#xff1a;NetSvc.cs 修改客户端脚本&#xff1a;Cli…

R语言绘制散点图

散点图是一种在直角坐标系中用数据点直观呈现两个变量之间关系、可检测异常值并探索数据分布的可视化图表。它是一种常用的数据可视化工具&#xff0c;我们通过不同的参数调整和包的使用&#xff0c;可以创建出满足各种需求的散点图。 常用绘制散点图的函数有plot()函数和ggpl…

ModBus Pull的详细安装教程

目录 一.导航 二 .安装 三.激活 四.使用 一.导航 modbus poll 和 modbus slave 是两种Modbus协议的软件工具 。 Modbus Poll&#xff1a;Modbus Poll 是一个客户端&#xff08;或主站&#xff09;软件&#xff0c;它允许用户与支持Modbus协议的设备进行通信。 Modbus Sla…

基于SPI的flash读写操作

1、实验目标 使用页写或连续写操作向Flash芯片写入数据&#xff0c;再使用数据读操作读取之前写入数据&#xff0c;将读取的数据使用串口传回PC机&#xff0c;使用串口助手传回数据并与之前写入数据比较&#xff0c;判断正误。 注意&#xff1a;在向Flash芯片写入数据之前&…

计算机毕业设计 Python医疗预约与诊断系统的设计与实现 Python毕业设计 Python毕业设计选题 Django Vue【附源码+安装调试】

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

【开源项目】Jsoncpp的简单使用

Jsoncpp是一个开源项目&#xff0c;它是一个用于处理JSON&#xff08;JavaScript Object Notation&#xff09;数据的C库。它支持将C结构化的数据转化为JSON字符串&#xff0c;也支持将JSON字符串转化为结构化数据 JSON&#xff08;JavaScript Object Notation&#xff09;数据…

基于springboot的大学生体质测试管理系统(含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于springboot的大学生体质测试管理系统1拥有三种角色 管理员&#xff1a;学生管理、教师管理、日常运行管理、运动分析管理、成绩管理、论坛管理、轮播图管理等 教师&#xff1a;登录…

C++AVL树详解

什么是AVL树 AVL树是最先发明的⾃平衡⼆叉查找树&#xff0c;AVL是⼀颗空树&#xff0c;或者具备下列性质的⼆叉搜索树&#xff1a;它的 左右⼦树都是AV树&#xff0c;且左右⼦树的⾼度差的绝对值不超过1。AVL树是⼀颗⾼度平衡搜索⼆叉树&#xff0c; 通过控制⾼度差去控制平衡…

算法-依据先序遍历和中序遍历构建二叉树

简单的二叉树遍历算法&#xff0c; 为了通过给定的先序遍历&#xff08;preorder&#xff09;和中序遍历&#xff08;inorder&#xff09;数组构造二叉树&#xff0c;我们需要理解这两种遍历方式的特点&#xff1a; 先序遍历&#xff08;Preorder&#xff09;&#xff1a;首先…

网站集群批量管理-Ansible(playbook)

1.剧本概述 1. playbook 文件,用于长久保存并且实现批量管理,维护,部署的文件. 类似于脚本存放命令和变量 2. 剧本yaml格式,yaml格式的文件:空格,冒号 2. 区别 ans-playbookans ad-hoc共同点批量管理,使用模块批量管理,使用模块区别重复调用不是很方便,不容易重复场景部署服务…

网关在不同行业自动化生产线的应用

网关在不同行业自动化生产线的应用&#xff0c;展示了其作为信息与物理世界交汇点的广泛影响力&#xff0c;尤其在推动行业智能化、自动化方面发挥了不可估量的作用。以下是网关技术在污水处理、智慧农业、智慧工厂、电力改造及自动化控制等领域的深入应用剖析。 1. 污水处理 …

java方法对象案例

完成电影信息展示功能&#xff1b;根据电影id查询该电影的详细 主方法&#xff1a; package Y; import java.util.Scanner; public class 模仿电影系统main { //目标&#xff1a;完成电影信息展示功能&#xff1b;根据电影id查询该电影的详细 //电影数据// 1,"水门桥&q…

超级智能“试衣镜”!GarDiff:高保真保持目标人物特征和服装细节,虚拟试穿技术新SOTA!

今天给大家介绍一个最新的虚拟试穿技术GarDiff&#xff0c;它可以分析你想穿的衣服和你的照片并提取出衣服的颜色、纹理和形状等细节。然后通过一个特殊的“对比器”来确保衣服与您的身体形状完美契合。这个对比器会使用两种不同的“眼睛”&#xff1a;一种是可以看到整体外观的…

PhotoMaker部署文档

一、介绍 PhotoMaker&#xff1a;一种高效的、个性化的文本转图像生成方法&#xff0c;能通过堆叠 ID 嵌入自定义逼真的人类照片。相当于把一张人的照片特征提取出来&#xff0c;然后可以生成你想要的不同风格照片&#xff0c;如写真等等。 主要特点&#xff1a; 在几秒钟内…

【华为HCIP实战课程七】OSPF邻居关系排错MTU问题,网络工程师

一、MTU MUT默认1500,最大传输单元,一致性检测 [R3-GigabitEthernet0/0/1]mtu 1503//更改R3的MTU为1503 查看R3和SW1之间的OSPF邻居关系正常: 默认华为设备没有开启MTU一致性检测! [R3-GigabitEthernet0/0/1]ospf mtu-enable //手动开启MTU检测 [SW1-Vlanif30]ospf mtu…

centos7 yum仓库无法使用的问题

1、问题 如下 2、按照csdn等网页说的做了没有用&#xff01;CentOS-yum源不可用报错&#xff1a;Could not retrieve mirrorlist 问题解决_yum could not retrieve mirrorlist-CSDN博客 3、使用b站博主的方法解决&#xff01; LinuxMirrors: GNU/Linux 一键更换系统软件源脚本…