回溯算法之N皇后

一   什么是回溯算法

回溯算法(Backtracking Algorithm)是一种用于解决组合优化问题的算法,它通过逐步构建候选解并进行验证,以寻找所有满足特定条件的解。回溯算法通常应用于在给定约束条件下枚举所有可能解的问题,如组合、排列、子集等。

回溯算法的基本思想是通过递归的方式进行搜索,每一步都尝试扩展当前的解,直到找到满足条件的解或者确定无解。在搜索的过程中,如果当前的解不满足约束条件,就会回溯到上一步进行其他选择,继续搜索。

回溯算法的一般步骤如下:

  1. 定义问题的解空间,确定问题的约束条件。
  2. 通过递归的方式搜索解空间,每一步都进行选择,并进行约束条件的检查。
  3. 如果当前的选择满足约束条件,则继续递归地进行下一步选择。
  4. 如果当前的选择不满足约束条件,进行回溯,撤销当前选择,返回上一步继续搜索其他选择。
  5. 当搜索完成后,得到所有满足条件的解。

回溯算法的时间复杂度通常较高,因为它需要枚举所有可能的解。在某些情况下,可以通过剪枝等优化策略来减少搜索空间,提高算法效率。

回溯算法在很多问题中都有应用,例如八皇后问题、0-1背包问题、图的遍历等。它是一种非常经典和常用的算法思想,对于解决组合优化问题具有重要的作用。

通常解该类题目时,我们要确定解的空间,从而很好的利用回溯算法来解决该类题目。


二   何为解空间

      解空间(Solution Space)是指在给定问题的约束条件下,所有可能的解的集合。它包含了问题的所有合法解。

      解空间的具体形式取决于问题的性质和约束条件。对于某些问题,解空间可能是一个有限的集合,例如在数独游戏中,解空间是由符合数独规则的所有数字填充方案组成的集合。而对于其他问题,解空间可能是一个无限的集合,例如在连续优化问题中,解空间是由实数构成的无限维空间。

     解空间是问题求解的关键概念之一。在解决问题时,我们通常需要在解空间中搜索满足特定条件的解。回溯算法、枚举法、剪枝算法等求解方法都是基于对解空间的搜索。

      解空间的大小直接影响了问题的复杂性和求解算法的效率。如果解空间非常大,问题的求解可能会非常困难,需要耗费更多的时间和资源。因此,在实际应用中,优化算法常常通过剪枝、启发式搜索等技术来减小解空间的规模,以提高求解效率。

总之,解空间是问题求解中描述所有可能解的概念,通过搜索解空间,我们可以找到满足问题要求的解或者找到最优解。

对于大多数该类问题的解空间都是树的形式,集合的大小构成了树的宽度,递归的深度构成的树的深度。


三   回溯算法的模板

下面是回溯算法的一般模板:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {//横向遍历
        处理节点;//这里一般可能进行剪枝操作
        backtracking(路径,选择列表); // 递归,纵向遍历
        回溯,撤销处理结果
    }
}

这是一个基本的回溯算法模板,其中的关键点包括:

  1. 定义终止条件:当满足终止条件时,表示找到了一个解或者不再需要继续搜索,可以进行相应的操作,如输出结果或返回。

  2. 遍历选择列表:遍历所有可能的选择,通常使用循环结构,对于每个选择,进行相应的操作。

  3. 做出选择:根据当前选择,更新状态或路径,表示对问题的一次选择。

  4. 递归进入下一层决策树:根据当前选择,进入下一层决策树,即进行下一步的选择。

  5. 撤销选择:在回溯到上一层之前,需要撤销当前选择,恢复状态或路径,以便进行下一个选择。

在实际应用中,根据具体问题的不同,模板中的代码需要进行相应的修改和扩展,以适应问题的特点和约束条件。同时,通过剪枝、优化等技巧,可以对模板进行改进,提高算法的效率。

需要注意的是,回溯算法是一种暴力搜索的方法,解空间的规模很大时,可能会导致算法效率低下。因此,在使用回溯算法时,需要根据问题的规模和特点进行合理的优化和剪枝,以提高算法的性能。


四   回溯算法例题之N皇后问题

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

4*4的棋盘摆放结果如下:

解题思路:

  1. 定义一个 N × N 的棋盘,使用一个二维数组或其他数据结构表示。初始化棋盘的所有位置为空。

  2. 从第一行开始,逐行放置皇后。对于每一行,遍历该行的每一个位置,尝试将皇后放置在当前位置。

  3. 在放置皇后之前,检查是否满足以下条件:

    • 当前位置的同一列没有其他皇后。
    • 当前位置的左上方和右上方(对角线)没有其他皇后。
  4. 如果满足上述条件,将皇后放置在当前位置,并将该位置标记为已占用。

  5. 继续递归地处理下一行,重复步骤 3 和步骤 4。

  6. 如果已经放置了 N 个皇后,表示找到了一个可行解,将该解保存起来。

  7. 回溯到上一步,撤销对当前位置的选择,继续尝试下一个位置。

  8. 当所有的位置都尝试完毕或者已经找到了所有的可行解时,算法结束。

  9. 返回所有的可行解。

    我们先以3*3的棋盘来看它的解空间,如图所示:

不难看出解空间对应的是树的结构,我们可以套用模板进行解决:

void Backtrack(char bord[][N],int n,int row){
	if(row>=n){//递归出口
		print(bord,n);//如果满足直接打印结果
		count++;//用来记录解的数量
		printf("\n");
		return ;
	}else{
		for(int colum=0;colum<n;colum++){//横向遍历
			if(isselect(bord,row,colum,n)){//如果满足放置条件
				bord[row][colum] = 'Q';
				Backtrack(bord,n,row+1);//进行递归,选择下一行的格子
				bord[row][colum] = '*';//回溯
			}
		}
	}
}

 【完整代码】

#include <stdio.h>
#include <stdbool.h>
#define N 100

int count=0;
void print(char bord[][N],int n){
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			if(bord[i][j] == 'Q'){
				printf("  Q");
			}else{
				printf("  *");
			} 
		}
		printf("\n");
	} 
}

bool isselect(char bord[][N], int row, int colum,int n) {
	for(int j=0;j<row;j++){//判断列
		if(bord[j][colum]=='Q'){
			return false;
		}
	}
	
	for(int i=row,j=colum;i>=0&&j>=0;i--,j--){//判断左上角
		if(bord[i][j]=='Q'){
			return false;
		}
		
	}
	
	for(int i=row,j=colum;i>=0&&j<n;i--,j++){//判断右上角
		if(bord[i][j]=='Q'){
			return false;
		}
		
	}
	return true;
	
}



void Backtrack(char bord[][N],int n,int row){
	if(row>=n){
		print(bord,n);
		count++;
		printf("\n");
		return ;
	}else{
		for(int colum=0;colum<n;colum++){
			if(isselect(bord,row,colum,n)){
				bord[row][colum] = 'Q';
				Backtrack(bord,n,row+1);
				bord[row][colum] = '*';
			}
		}
	}
}

int main()
{
	int n;
	printf("请输入皇后个数:\n");
	scanf("%d",&n);
	char bord[N][N]={'*'};
	printf("皇后摆放形式如下:\n");
	Backtrack(bord,n,0);
	printf("%d后问题共有%d种摆放方案 ",n,count);
	return 0;
}

【运行效果】

部分资料参考:代码随想录; 

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

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

相关文章

FaceBook推出新的翻译模型Seamless!可实现跨语言交流的无缝衔接!

FaceBook **&#xff08;中文名&#xff1a;脸书&#xff09;**近期发布了一个新的翻译模型 Seamless Communication&#xff0c;可实现跨语言实时"无缝"交流。 该模型可以保留跨语言的表达方式和复杂性&#xff08;翻译时保留语音中的停顿和语速&#xff0c;以及声…

优雅草蜻蜓I即时通讯·水银版私有化部署之安卓Android端编译-02

Android 项目配置 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 使用以上Android studio版本 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 下载最低sdk最低版本28 完成后就可以导入项目(项目导入不能开VPN,会导致部分三方库…

Glibc之malloc实现原理

前言导入 内存管理之虚拟内存空间 详细了解这部分知识&#xff0c;再看下面的内容会很舒服 进程地址空间 在32位Linux系统中&#xff0c;进程地址空间是这样分布的。其中内核空间独占1G&#xff0c;不允许用户操作&#xff0c;其余3G由用户操作。malloc的操作对象&#xff1…

C语言之函数设计(1)

目录 没有返回值的函数 通用性 不含形参的函数 函数返回值的初始化 作用域 文件作用域 声明和定义 函数原型声明 头文件和文件包含指令 在上节中我们简单的学习了函数的创建方法&#xff08;函数定义&#xff09;与函数的使用方法&#xff08;函数调用&#xff09;&…

谈一谈网络协议中的应用层

文章目录 一&#xff0c;什么是HTTPHTTP的优缺点HTTPS 一&#xff0c;什么是HTTP 我们在通过网络进行传输数据时&#xff0c;我们要保证&#xff0c;我们在发送时构造的数据&#xff0c;在接收时也能够解析出来&#xff0c;这本质上就是一种协议&#xff0c;是一种应用层协议&…

python zblog API实现类似XMLRPC/发布文章

我发现python对Zblog的XML发布并不友好&#xff0c;虽然也有对应的模块&#xff0c;但是远远没有XPCRPC更直接方便&#xff0c;但是使用xmlRpc是直接给发布文章带来了不小的便利&#xff0c;但是对系统也并不友好&#xff0c;但是zblog也开放了Api&#xff0c;但是干部子弟不乐…

测试剪切板贴图,兼测试2023年12月7日更新的Bard

当前的情况好比&#xff0c;&#xff08;居然真的可以通过剪切板把图片放进来&#xff01;&#xff09; 听说2023年12月7日Bard有更新&#xff0c;所以&#xff0c;再测试了一次。这下&#xff0c;对大语言模型应该死心了&#xff1b;AI替代人的传闻应该是过早危言耸听了。

SAP UI5 walkthrough step3 Controls

在上一步&#xff0c;我们是直接用index.html 中的body 里面的DIVision去输出 hello world&#xff0c; 在这个章节&#xff0c;我们将用SAP UI5 的标准控件 sap/m/Text 首先&#xff0c;我们去修改 webapp/index.html <!DOCTYPE html> <html> <head><…

Vue3-02-ref() 响应式详解

ref() 是什么 ref() 是一个函数&#xff1b; ref() 函数用来声明响应式的状态&#xff08;就是来声明变量的&#xff09; ref() 函数声明的变量&#xff0c;是响应式的&#xff0c;变量的值改变之后&#xff0c;页面中会自动重新渲染。ref() 有什么特点 1.ref() 可以声明基础…

java服务调用mysql报错

一、前言 前端服务调用后端服务时出现以下报错&#xff0c;原因是使用mysql5.7版本数据库中存在ONLY_FULL_GROUP_BY这个配置项导致的不兼容 MySQLSyntaxErrorException: Expression #32 of SELECT list is not in GROUP BY clause and contains nonaggregated column demeter…

打造Github首页的动态飞线效果

一、导语 Github首页的地球动态飞线&#xff0c;大家都比较熟悉吧 二、分析 由大量随机的3点构造出贝塞尔曲线&#xff0c;然后开始从起点到终点的飞行后&#xff0c;然后再从起点到终点的消失&#xff0c;就此完成整个过程 三、基础代码 createCurve(startPoint, endPoint…

layui实现下拉框多选

引用layui第三方扩展实现下拉框选择渲染 第三方插件地址xmSelect下拉多选 xmSelect 实现效果 //第三方扩展插件 <script type"text/javascript" src"${ctx }/config/layui/dist/xm-select.js"></script> //jquery渲染 <script type&qu…

【数电笔记】58-同步D触发器

目录 说明&#xff1a; 1. 电路组成 2. 逻辑功能 3. 特性表、特性方程 4. 状态转移图 例题 5. 同步D触发器的特点 6. 集成同步D触发器&#xff1a;74LS375 74LS375内部原理 说明&#xff1a; 笔记配套视频来源&#xff1a;B站本系列笔记并未记录所有章节&#xff0c;…

JRT文件服务实现

网站与客户端打印和导出方面已经无大碍了&#xff0c;今天抽时间整整文件服务&#xff0c;文件服务设计可以查看下面连接。原理一样&#xff0c;代码会有些变化。 文件服务设计 首先实现文件服务的服务端&#xff0c;就是一个业务脚本&#xff0c;用来接收上传、移动和删除文件…

Qt之基于QMediaPlayer的音视频播放器(支持常见音视频格式)

Qt自带了一个Media Player的例子,如下图所示: 但是运行这个例子机会发现,连最基本的MP4格式视频都播放不了。因为QMediaPlayer是个壳(也可以叫框架),依赖本地解码器,视频这块默认基本上就播放个MP4,甚至连MP4都不能播放,如果要支持其他格式需要下载k-lite或者LAVFilte…

Qt槽函数不响应不执行的一种原因:ui提升导致重名

背景&#xff1a; 一个包含了组件提升的ui&#xff0c;有个按钮的槽函数就是不响应&#xff0c;于是找原因。 分析&#xff1a; 槽函数的对应一是通过connect函数绑定信号&#xff0c;二是on_XXX_signal的命名方式。界面上部件的槽函数通常是第二种。 我反复确认细节&#…

cache教程 4.一致性哈希(hash)

本章节是单节点走向分布式节点的一个重要部分。 一致性哈希(consistent hashing)的原理以及为什么要使用一致性哈希。实现一致性哈希代码&#xff0c;添加相应的测试用例 1.多节点部署遇到的问题 上一章节完成了一个单节点的缓存服务器。那对于一个单节点来说&#xff0c;读…

L1-031:到底是不是太胖了

题目描述 据说一个人的标准体重应该是其身高&#xff08;单位&#xff1a;厘米&#xff09;减去100、再乘以0.9所得到的公斤数。真实体重与标准体重误差在10%以内都是完美身材&#xff08;即 | 真实体重 − 标准体重 | < 标准体重10%&#xff09;。已知 1 公斤等于 2 市斤。…

CSS入门(样式表|class类|选择器|背景|!important|文本颜色|字体|注释)

为什么学习CSS&#xff0c;因为QSS vs CSS 相似度极高&#xff0c;学好CSS对于QSS和QML都有潜移默化的作用。记住不管学习什么&#xff0c;我们都在围绕Qt集成。 入门 介绍 CSS 功能丰富&#xff0c;不仅仅是布局页面 外部样式表 <link> 在给定的HTML代码中&#xff…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《考虑移动式储能调度的配电网灾后多源协同孤岛运行策略》

这篇文章的标题表明研究的主题是在配电网发生灾害后&#xff0c;采用一种策略来实现多源协同孤岛运行&#xff0c;并在这个过程中特别考虑了移动式储能的调度。 让我们逐步解读标题的关键词&#xff1a; 考虑移动式储能调度&#xff1a; 文章关注的焦点之一是移动式储能系统的…