数据结构与算法:回溯算法约束条件:剪枝详解、示例(C#、C++)与回溯典型例题详解

文章目录

  • 一、约束条件
  • 二、剪枝
  • 三、典型例题
  • 四、常用术语
  • 五、示例
    • N 皇后问题 C# 示例
    • N 皇后问题 C++ 示例
  • 六、常见用用回溯算法解决的问题汇总
    • 组合问题:
    • 图论问题:
    • 棋盘游戏问题:
    • 优化问题:
    • 调度问题:
    • 其他问题:
  • 总结

在这里插入图片描述


回溯算法是一种通过探索所有可能的候选解来找出所有解的算法。在解决一些问题时,我们需要设置一些约束条件,以确保候选解的有效性。这些约束条件在算法中起着非常重要的作用,因为它们定义了一个问题的解空间。通常,我们会使用剪枝技术来减少搜索空间,以提高算法的效率。

本文将详细介绍回溯算法中的约束条件、剪枝技术以及一些典型的回溯问题,还会讨论一些常用的术语。

一、约束条件

在回溯算法中,约束条件是非常重要的,因为它们定义了一个问题的解空间。约束条件必须被满足,一个候选解才被认为是有效的。通常,这些约束条件在算法中被用来进行剪枝,即提前排除那些明显不可能产生解的候选解,从而减少搜索空间。

以 N 皇后问题为例,约束条件如下:

  1. 同一列上的两个皇后不能相互攻击。
  2. 同一斜线(对角线和反对角线)上的两个皇后不能相互攻击。

在 0-1 背包问题中,约束条件如下:

  1. 背包的总容量有限。
  2. 每个物品都有一个重量和价值。

二、剪枝

剪枝是回溯算法中用于减少搜索量的技术。有两种主要的剪枝技术:

  • 前剪枝: 在搜索的早期阶段就排除一些不可能产生有效解的分支。例如,在解决 N 皇后问题时,如果一个皇后已经被放置在某个位置,那么与这个位置在同一行、同一列和同一对角线上的所有其他位置都不能放置皇后。

  • 后剪枝: 在搜索的后期阶段消除那些已经确定不可能产生解的分支。例如,在解决 0-1 背包问题时,如果当前的总重量已经超过背包的容量,那么这个分支可以被剪掉,因为不可能产生一个更优的解。

三、典型例题

1. N 皇后问题: 在 N×N 的棋盘上放置 N 个皇后,使得它们不会相互攻击(即没有两个皇后在同一列、同一行或同一对角线上)。
2. 0-1 背包问题: 给定一组物品,每个物品有一个价值和一个重量,需要选择一些物品放入一个给定容量的背包中,使得背包内物品的总价值最大。
3. 旅行商问题(TSP): 给定一组城市和每两个城市之间的距离,找到一条最短的路径,访问每个城市一次并返回起点。

四、常用术语

1. 候选解: 一个潜在的解,它可能满足所有约束条件。
2. 有效解: 一个候选解,它满足所有约束条件,被认为是实际问题中的解。
3. 搜索空间: 所有可能候选解的集合。
4. 路径/分支: 从初始状态到某个状态的一系列决策的集合。
5. 深度优先搜索(DFS): 一种回溯算法的实现方式,它沿着一个分支深入到不能再深入为止,然后回溯到上一个分叉点继续搜索。

五、示例

下面是 N 皇后问题和 0-1 背包问题的 C# 和 C++ 示例代码。

N 皇后问题 C# 示例

using System;
using System.Collections.Generic;namespace NQueens
{class Program{static void Main(string[] args){int n = 8;SolveNQueens(n);}static void SolveNQueens(int n){int[] board = new int[n];bool[] columns = new bool[n];bool[] diag1 = new bool[2 * n - 1];bool[] diag2 = new bool[2 * n - 1];if (PlaceQueens(board, 0, columns, diag1, diag2)){Console.WriteLine("解决方案:");PrintBoard(board);}else{Console.WriteLine("没有找到解决方案。");}}static bool PlaceQueens(int[] board, int row, bool[] columns, bool[] diag1, bool[] diag2){if (row == board.Length){return true;}for (int col = 0; col < board.Length; col++){if (columns[col] || diag1[row - col + board.Length - 1] || diag2[row + col]){continue;}columns[col] = true;diag1[row - col + board.Length - 1] = true;diag2[row + col] = true;board[row] = col;if (PlaceQueens(board, row + 1, columns, diag1, diag2)){return true;}board[row] = 0;columns[col] = false;diag1[row - col + board.Length - 1] = false;diag2[row + col] = false;}return false;}static void PrintBoard(int[] board){for (int i = 0; i < board.Length; i++){for (int j = 0; j < board.Length; j++){Console.Write(board[j] == i ? "Q " : ". ");}Console.WriteLine();}}}
}

N 皇后问题 C++ 示例

#include <iostream>
#include <vector>using namespace std;void printBoard(const vector<vector<int>>& board) {for (const auto& row : board) {for (int column : row) {cout << column << " ";}cout << endl;}
}bool isSafe(const vector<vector<int>>& board, int row, int col, vector<bool>& columns, vector<bool>& diag1, vector<bool>& diag2) {for (int i = 0; i < row; i++) {if (board[i][col] == 1) {return false;}}for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) {if (board[i][j] == 1) {return false;}}for (int i = row, j = col; i < board.size() && j < board[0].size(); i++, j++) {if (board[i][j] == 1) {return false;}}return true;
}bool solveNQueensUtil(vector<vector<int>>& board, int row, vector<bool>& columns, vector<bool>& diag1, vector<bool>& diag2) {if (row == board.size()) {printBoard(board);return true;}for (int col = 0; col < board[0].size(); col++) {if (isSafe(board, row, col, columns, diag1, diag2)) {board[row][col] = 1;columns[col] = true;diag1[row - col + board.size() - 1] = true;diag2[row + col] = true;if (solveNQueensUtil(board, row + 1, columns, diag1, diag2)) return true;board[row][col] = 0;columns[col] = false;diag1[row - col + board.size() - 1] = false;diag2[row + col] = false;}}return false;
}vector<vector<int>> solveNQueens(int n) {vector<vector<int>> board(n, vector<int>(n, 0));vector<bool> columns(n, false);vector<bool> diag1(2 * n - 1, false);vector<bool> diag2(2 * n - 1, false);solveNQueensUtil(board, 0, columns, diag1, diag2);return board;
}int main() {int n = 4;vector<vector<int>> board = solveNQueens(n);return 0;
}

六、常见用用回溯算法解决的问题汇总

回溯算法是一种深度优先搜索的变种,它适用于解决那些需要探索所有可能解的问题。这类问题通常具有递归结构,即一个问题的解空间可以被分解为多个子问题,每个子问题都是原问题的一部分。以下是一些可以用回溯算法解决的问题:

组合问题:

  1. 排列问题(Permutations):给定一组数字,找出所有可能的排列。
  2. 组合问题(Combinations):给定一组数字,找出所有可能的组合。

图论问题:

  1. 最小生成树(MST):在无向图中找到一个包含所有顶点的子图,使得边的总权重最小。
  2. 最大匹配(Maximum Matching):在图中发现最大的匹配集合。
  3. 哈密顿路径(Hamiltonian Path):在图中寻找一条经过所有顶点恰好一次的路径。
  4. 中国邮递员问题(Chinese Postman Problem):寻找一条经过所有边恰好一次的路径,使得总权重最小。

棋盘游戏问题:

  1. 八皇后问题(8 Queens):在 8x8 的棋盘上放置 8 个皇后,使它们互不攻击。
  2. 骑士巡游问题(Knight’s Tour):在棋盘上找到一条骑士访问所有方格恰好一次的路径。

优化问题:

  1. 0-1 背包问题(0-1 Knapsack Problem):给定一组物品,每个物品有一个价值和重量,选择一些物品放入一个给定容量的背包中,使得背包内物品的总价值最大。
  2. 旅行商问题(TSP):寻找一条最短的路径,访问每个城市恰好一次并返回起点。
  3. 表达式求值问题(Evaluate Expression):给定一个包含加、减、乘、除和括号的表达式,计算其值。

调度问题:

  1. 课程调度问题(Course Scheduling):在有限的时间内安排多门课程,满足各种约束条件。
  2. 机器调度问题(Machine Scheduling):在有限的时间内安排多个机器的工作任务,满足各种约束条件。

其他问题:

  1. 子集和问题(Subset Sum):给定一个整数数组和一个目标值,判断是否存在一个子集,其和等于目标值。
  2. 数独问题(Sudoku):在 9x9 的网格中填入数字,使得每行、每列和每个 3x3 子网格中都包含 1 到 9 的所有数字。
  3. 汉诺塔问题(Tower of Hanoi):通过移动盘子从一个塔到另一个塔,同时遵守特定的规则。

回溯算法通过递归地尝试所有可能的解,并在发现当前解不满足要求时回溯到上一个状态,尝试其他可能的解。这种方法适用于解决上述问题,并且可以通过剪枝技术来优化搜索过程,减少不必要的计算。

总结

回溯算法是一种强大的算法,可以用来解决各种问题。通过设置约束条件和使用剪枝技术,我们可以有效地减少搜索空间,提高算法的效率。在实际应用中,回溯算法可以帮助我们解决各种问题,如 N 皇后问题、0-1 背包问题、旅行商问题等。希望这篇博客能帮助你更好地理解回溯算法及其应用。

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

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

相关文章

How to persist LangChain conversation memory (save and load)

题意&#xff1a;如何持久化 LangChain 对话记忆&#xff08;保存和加载&#xff09; 问题背景&#xff1a; Im creating a conversation like so: 我正在创建一个对话&#xff0c;如下所示&#xff1a; llm ChatOpenAI(temperature0, openai_api_keyOPENAI_API_KEY,…

【CTF】BUU BURP COURSE 11

打开靶机之后&#xff0c;显示只能在本地打开&#xff08;一度以为靶机出问题&#xff09;。 解题步骤&#xff1a; 1.分析请求包信息 2.构建本地请求IP X-Real-IP&#xff1a;记录真实客户端IP地址信息&#xff1b; X-Forward-for&#xff1a;记录了请求IP到目标ip所经历的…

新型基坑气膜:施工开挖的得力干将—轻空间

随着城市建设的加速推进&#xff0c;施工过程中的环境问题日益受到关注。新型基坑气膜以其卓越的防尘、降噪、节能和防火功能&#xff0c;成为施工开挖领域中的得力干将&#xff0c;极大地提升了绿色施工的水平。 基坑气膜的作用 基坑气膜在施工现场形成了一个完全封闭的作业空…

Java EE之Servlet

Servlet 是 Java EE&#xff08;Java Platform, Enterprise Edition&#xff09;规范中的一个技术&#xff0c;是服务器端 Java 程序&#xff0c;用于处理客户端请求并生成动态响应。Servlet 通常用于构建 Web 应用程序&#xff0c;并与 HTTP 协议紧密集成。以下是对 Servlet 的…

JavaWeb系列七: 动态WEB开发核心(Servlet) 下

韩老师学生 ServletConfigServletContext网站计数器 HttpServletRequest细节1细节2细节3 Dispathcer请求转发应用实例请求转发细节和注意事项习题 HttpServletResponse请求重定向请求重定向注意事项动态获取到application context练习题 ServletConfig ●ServletConfig基本介绍…

docker --restart 容器重启策略

官网连接&#xff1a;https://docs.docker.com/config/containers/start-containers-automatically/ 当容器退出后&#xff0c;或者docker程序重启了&#xff0c;容器是否要重启&#xff0c;可以用重启策略控制。 用docker run命令的时候&#xff0c;用--restart 设置容器重启…

1.文件上传漏洞渗透及防御(OWASP实战训练)

1.文件上传漏洞渗透及防御&#xff08;OWASP实战训练&#xff09; OWASPupload上传漏洞实验一&#xff1a;低安全模式下&#xff0c;上传任意类型的文件&#xff0c;文件大小不受限制实验二&#xff0c;安全级别调整将其变为中等安全级别实验三&#xff1a;将其设为高安全级别 …

【教程】如何一步一步训练一个SOM神经网络-自组织竞争神经网络(Self-organizing Feature Map)

本文来自《老饼讲解-BP神经网络》https://www.bbbdata.com/ 目录 一、什么是SOM神经网络1.1.SOM神经网络有什么用1.2.SOM神经网络是如何聚类的 二、如何训练一个SOM神经网络2.1. 训练一个SOM神经网络的代码示例2.2. 如何查看SOM神经网络的聚类中心 SOM神经网络全称为自组织竞争…

《系统架构设计师教程(第2版)》第11章-未来信息综合技术-05-数字孪生体(Digital Twin)技术概述

文章目录 1. 数字孪生体发展历程1.2 准备期1.2 概念产生期1.3 领先应用期1.4 深度开发和大规模扩展应用期 2. 数字孪生体的定义3. 数字孪生体的关键技术3.1 建模3.2 仿真技术3.3 其他技术 4. 数字孪生体的应用4.1 制造领域4.2 全产业链上的应用4.3 城市4.4 战场 1. 数字孪生体发…

解决Java中的NoSuchAlgorithmException异常的技术实践

解决Java中的NoSuchAlgorithmException异常的技术实践 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在Java编程中&#xff0c;NoSuchAlgorithmException异常…

Redis-主从复制-测试主从模式下的读写操作

文章目录 1、在主机6379写入数据2、在从机6380上写数据报错3、从机只能读数据&#xff0c;不能写数据 1、在主机6379写入数据 127.0.0.1:6379> keys * (empty array) 127.0.0.1:6379> set uname jim OK 127.0.0.1:6379> get uname "jim" 127.0.0.1:6379>…

【机器学习】python之人工智能应用篇——3D生成技术

在Python中&#xff0c;人工智能&#xff08;AI&#xff09;与3D生成技术的结合可以体现在多个方面&#xff0c;比如使用AI算法来优化3D模型的生成、通过机器学习来预测3D模型的属性&#xff0c;或者利用深度学习来生成全新的3D内容。然而&#xff0c;直接通过AI生成完整的3D模…

单片机IO口模拟串口实现原理

参考链接 1、使用GPIO来模拟UART 2、STM32之IO模拟串口篇 1、工作原理 单片机IO口模拟串口的实现原理通常是通过软件来模拟串行通信的传输和接收。下面说明了单片机IO口模拟串口的实现原理&#xff1a; 配置IO口&#xff1a;选择两个IO口作为模拟串口的发送和接收引脚。通常…

go语言:数据库sql查询保存任意数量字段的数据

1、查询任意列数的表&#xff0c;并输出 func search() {rows, _ : db.Query("select * from users") // 查询数据columns, _ : rows.Columns() // 查询到的字段名列表values : make([]any, len(columns)) // 根据字段数量&#xff0c;创建接收…

优雅的参数校验——Guava库中的Preconditions

Guava库中的Preconditions类提供了一些静态方法&#xff0c;用于在程序中执行参数的检查和验证。这些方法在编写健壮和可维护的代码时非常有用 checkArgument(boolean expression)&#xff1a; 作用&#xff1a;用于验证方法的参数是否满足某个条件。如果条件不满足&#xff0c…

rancher快照备份至S3

巧用rancher的S3快照备份功能&#xff0c;快速实现集群复制、集群转移、完全崩溃后的极限修复 1.进入集群管理&#xff0c;在对应的集群菜单后&#xff0c;点击编辑配置 2.选择ETCD&#xff0c;启用&#xff0c;Backup Snapshots to S3选项 并填入你的minio 3 配置成功后 手…

使用adb shell getprop命令获取Android设备的属性

常用属性获取&#xff1a; adb shell getprop ro.build.version.emui —查询EMUI版本 adb shell getprop ro.product.brand —查询手机品牌 adb shell getprop ro.product.name --查询设备名称 adb shell getprop ro.serialno —查询设备序列号 获取手机系统信息( CPU,厂商…

C# 信号量的使用

学习来源&#xff1a;《.net core 底层入门》 第六章第9节&#xff1a;信号量 案例&#xff1a;主线程负责添加数据&#xff0c;子线程负责获取数据 使用SemaphoreSlim&#xff08;轻信号量&#xff09;实现&#xff1a; using System; using System.Collections.Generic; us…

python中的<class ‘complex‘>

一般编程里面不怎么会讲&#xff0c;但是还是挺强大的一个类。 在 Python 中&#xff0c;<class complex> 表示复数类型。复数是一种包含实部和虚部的数学数&#xff0c;可以用 a bj 的形式表示&#xff0c;其中 a 表示实部&#xff0c;b 表示虚部&#xff0c;j 是虚数…

bat脚本暂停写法

使用 timeout 命令来实现暂停&#xff1a; timeout /t 1 /nobreak >nul/t &#xff1a;指定暂停的时间&#xff0c;单位为秒。/nobreak&#xff1a;防止用户按任意键终止暂停。