算法学习笔记之回溯法学习

博客介绍了回溯法解题的通常步骤,包括定义解空间、确定结构和深度优先搜索并剪枝。还阐述了基本回溯策略,如递归回溯和迭代回溯。以n皇后问题为例,给出问题描述和代码实现,最后拓展了回溯法搜索排列树算法框架。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.回溯法解题的通常步骤:

  1. 针对所给的问题,定义问题的解空间
  2. 确定易于搜索的解空间结构
  3. 以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索

2.基本的回溯策略【也可以说是编程框架】

a.递归回溯

参数t表示深度,这一搜索过程按深度优先方式进行,调用一次Backtrace(1)即可完成整个回溯搜索过程。

void Backtrace(int t) {
	if (t > n)
		//可行解
		Output(x);
	else
		//遍历第t层的所有节点
		//f(n,t)起始编号,g(n,t)终止编号
		for (int i = f(n, t);i < g(n, t);i++) {
			x[t] = h[i];
			//满足约束条件和边界条件,遍历下一层
			if (Constraint(t) && Bound(t))
				Backtrace(t + 1);
		}
}

b.迭代回溯

参数t表示深度

void IterativeBacktrace(void) {
	int t = 1;
	while (t > 0) {
		if (f(n, t) <= g(n, t))
			//遍历
			for (int i = f(n, t);i <= g(n, t);i++) {
				x[t] = h[i];
				//剪枝
				if (Constraint(t) && Bound(t)) {
					//Solution(t)判断当前节点是否已经得到问题的可行解
					if (Solution(t))
						Output(x);
					else
						t++;
				}
			}
		else
			t--;
	}
}

3.n皇后问题

问题描述

n*n棋盘上放n个皇后,皇后不能在同一行同一列或者同一斜线出现。
若两个皇后位置分别为(i,j)and (k,l),那么

i!=j,k!=l,i-j!=k-l,i+j!=k+l  //约束条件
i,j,k,l<=n  //边界条件

具体实现的代码如下:

#include <math.h>
#include <iostream>

class Queen {
	//友元有权访问该类的所有成员
	friend int nQueen(int);
private:
	bool Place(int k);
	void Backtrace1(int t);
	void Backtrack2(void);
	int n,//皇后个数
		*x;//当前解
	long sum;//当前已找到的可行方案数
};
bool Queen::Place(int k) {
	for (int j = 1;j < k;j++)
		if (abs(k - j) == abs(x[j] - x[k]) || x[j] == x[k])
			return false;
	return true;
}
void Queen::Backtrace1(int t) {
	if (t > n)
		sum++;
	else {
		for (int i = 1;i <= n;i++) {
			x[t] = i;
			if (Place(t))
				Backtrace1(t + 1);
		}
	}
}
void Queen::Backtrack2(void) {
	x[1] = 0;
	int k = 1;
	while (k > 0) {
		x[k] += 1;
		//剪枝
		while ((x[k] <= n) && !(Place(k))) x[k] += 1;
		if (x[k] <= n) {
			if (k == n){
				std::cout << "-----------------------------"<<std::endl;
				for (int i = 1;i <= n;i++) {
					for (int j = 1;j < x[i];j++)
						std::cout << "*";
					std::cout << "a" << std::endl;
				}
			sum++;
			}
			else {
				k++;
				x[k] = 0;
			}
		}
		else k--;//不满足条件后的回溯
	}
}
int nQueen(int n) {
	Queen X;
	//初始化X
	X.n = n;
	X.sum = 0;
	int *p = new int[n + 1];
	for (int i = 0;i <= n;i++)
		p[i] = 0;
	X.x = p;
	X.Backtrace1(1);
	//X.Backtrack2();
	delete[]p;
	return X.sum;
}
int main() {
	int sum = nQueen(7);
	std::cout << sum << std::endl;
	system("pause");
	return 0;
}

个人感觉回溯法就像是一个穷举过程,只不过在枚举过程中增加了一些提前剪枝的过程。递归法的实现更容易理解,但是迭代法可以培养思维,或许对解动态规划有点帮助。至于算法复杂度应该差不多吧,毕竟都是遍历了所有的可能解。

拓展一下(回溯法搜索排列树算法框架)

排列树:所给的问题是确定n个元素满足某种性质的排列时,相应的解空间称为排列树。

void Backtrack(int t) {
	if (t > n)
		Output(x);
	else
		for (int i =t;i <= n;i++) {
			Swap(x[t],x[i]);
			if (Constraint(t) && Bound(t))
				Backtrace(t + 1);
			Swap(x[t],x[i])
		}
}

在调用Backtrack(1)执行回溯之前,先将变量数组x初始化为单位阵列(1,2,…,n)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值