携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第26天,点击查看活动详情
什么是回溯算法?
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
递归伴随着回溯
在递归的过程中,我们会经历回溯的过程
回溯的模板是这样的:
回溯法解决的问题都可以抽象为树形结构。
因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度,都构成的树的深度。
讲讲真题:
7-6 全排列(回溯,字典序)(30 分)
题目描述:
对于1~n这n个不同的数,按照一定的顺序把这n个数排列起来(每个数出现一次,且不重复, n<10),将所有的排列列出,称为全排列。
输入格式:
一个数n。
输出格式:
1~n的全排列,每个排列一行(按字典序输出)。
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
代码长度限制 16 KB
时间限制 400 ms
内存限制 64 MB
思路分析:
跟着回溯算法的思路,我们写一个backstaring的回溯函数(核心)
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
vector<vector<int>> result; // 全部结果集合
vector<int> path; // 递归树上一条路径的结果
vector<int> nums; // 存储[1,n]
int n;
void backstaring(const vector<int>& nums, vector<bool>& used) {
// 终止条件&收集结果
if(path.size() == nums.size()) {
result.push_back(path);
return ;
}
for(int i = 0; i < nums.size(); i++) {
// nums[i]已经使用过了 就跳过本轮循环
if(used[i] == true) continue;
// 没使用过 做出选择先标记为使用过
used[i] = true;
path.push_back(nums[i]);
backstaring(nums, used);
// 撤销选择
path.pop_back();
used[i] = false;
}
}
int main() {
FAST_IO;
cin >> n;
nums.resize(n);
// 初始化nums数组
for(int i = 1; i <= n; i++) nums[i-1] = i;
// used数组用来判断某元素是否已经被使用过了
vector<bool> used(nums.size(), false);
backstaring(nums, used);
for(int i = 0; i < result.size(); i++) {
for(int j = 0; j < result[i].size(); j++) {
cout << result[i][j] << " ";
}
cout << "\n";
}
return 0;
}
PS:成功解题=理清思路+一定的技巧~