简介
- 回溯算法基于深度优先搜索,实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
- 由于非递归式回溯算法较难实现,本文只介绍递归式回溯。
回溯算法框架
int a[n+1];//这里用下标1~n
void DFS(int i){
//搜索第i层
if(i>n){
//搜索结束
//结果处理(输出结果,方案计数等)
}
for(int j=下界;j<=上界;j++){
if(限界函数&&约束条件){
//满足限界函数和约束条件
x[i]=j;//保存当前层的结果
...//回溯入场准备工作
DFS(i+1);//搜索下一层
...//回溯退回清理工作
}
}
}
例题
打靶问题
题目描述
一个人打 10 10 10次靶(范围在 0 0 0环到 10 10 10环),问这 10 10 10次打靶之后,共中 90 90 90环的情况的个数。
输入格式
无
输出格式
输出中 90 90 90环的个数。
样例
无
参考题解
#include <iostream>
using namespace std;
int cnt=0;
void DFS(int k,int v){
//k表示打第i次靶,v表示前i-1次靶得分
if(k==10){
if(v+10>=90)//第10环满分,总分大于等于90说明有解
cnt++;//结果+1
return;
}
for(int i=0;i<=10;i++){
//枚举第i次打靶得分
if(v+i+10*(10-k)>=90){
//如果后面几次都得满分达到90分才有解
DFS(k+1,v+i);//搜索k+1层,更新得分v+i
}
}
}
int main(){
DFS(1,0);
cout<<cnt;//输出方案数
return 0;
}
全排列1
题目描述
从 n n n个不同元素中任取 m ( m ≤ n ) m(m≤n) m(m≤n)个元素,按照一定的顺序排列起来,叫做从 n n n个不同元素中取出 m m m个元素的一个排列。
输入格式
一行一个正整数 n n n.
输出格式
输出所有排列,每个数字之间以空格隔开,每个结果之间以换行隔开。
样例输入
3
样例输出
1 1 1
1 1 2
1 1 3
1 2 1
1 2 2
1 2 3
1 3 1
1 3 2
1 3 3
2 1 1
2 1 2
2 1 3
2 2 1
2 2 2
2 2 3
2 3 1
2 3 2
2 3 3
3 1 1
3 1 2
3 1 3
3 2 1
3 2 2
3 2 3
3 3 1
3 3 2
3 3 3
数据范围与提示
1 ≤ n ≤ 7 1\le n \le 7 1≤n≤7
参考题解
#include <iostream>
using namespace std;
int x[8],n;
void DFS(int k){
//k表示第k个数的搜索
if(k>n){
//搜索结束,输出x[]数组
for(int i=1;i<=n;i++){
printf("%d ",x[i]);
}
printf("\n");
return;
}
for(int i=1;i<=n;i++){
//枚举x[k]
x[k]=i;//标记x[k]=i
DFS(k+1);//搜索下一层
}
}
int main(){
cin>>n;
DFS(1);
return 0;
}
全排列2
题目描述
把 1 ∼ n 1\sim n 1∼n这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序。
输入格式
一个整数 n n n。
输出格式
按照从小到大的顺序输出所有方案,每行 1 1 1个。
首先,同一行相邻两个数用一个空格隔开。
其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。
输入样例
3
输出样例
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
数据范围与提示
1 ≤ n ≤ 9 1\le n \le 9 1≤n≤9
参考题解
#include <iostream>
#include <algorithm>
using namespace std;
int x[10],visit[10],n;
void DFS(int k){
//k表示第k个数的搜索
if(k>n){
//搜索结束,输出x[]数组
for(int i=1;i<=n;i++){
printf("%d ",x[i]);
}
printf("\n");
return;
}
for(int i=1;i<=n;i++){
//枚举x[k]
if(!visit[i]){
//如果i未被访问
x[k]=i;//标记x[k]=i
visit[i]=1;//i标记1表示访问了
DFS(k+1);//搜索下一层
visit[i]=0;//回溯回退,标记i为0表示访问
}
}
}
int main(){
fill(visit,visit+10,0);//标记visit为0表示未访问
cin>>n;
DFS(1);
return 0;
}
组合的输出
题目描述
排列与组合是常用的数学方法,其中组合就是从 n n n个元素中抽出 r r r个元素(不分顺序且 r ≤ n r\le n r≤n),我们可以简单地将n个元素理解为自然数 1 , 2 , … , n 1,2,\dots ,n 1,2,…,n,从中任取 r r r个数。
现要求你用递归的方法输出所有组合。
例如 n = 5 , r = 3 n=5,r=3 n=5,r=3,所有组合为:
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
参考题解
#include <iostream>
#include <algorithm>
using namespace std;
int x[21],visit[21],n,r;
void DFS(int k){
//k表示第k个数的搜索
if(k>r){
//搜索结束,输出x[]数组
for(int i=1;i<=r;i++){
printf("%d ",x[i]);
}
printf("\n");
return;
}
for(int i=x[k-1]+1;i<=n;i++){
//枚举x[k],输出组合数不能有重复,当x[i]>x[i-1]时保证无重复
if(!visit[i]){
//如果i未被访问
x[k]=i;//标记x[k]=i
visit[i]=1;//i标记1表示访问了
DFS(k+1);//搜索下一层
visit[i]=0;//回溯回退,标记i为0表示访问
}
}
}
int main(){
x[0]=0;//为了方便下界使用,见DFS第二个for循环
fill(visit,visit+21,0);//标记visit为0表示未访问
cin>>n>>r;
DFS(1);
return 0;
}
排列的生成
题目描述
给出 n n n 和 m m m,请编程按字典序输从 1 , 2 , … , n 1,2,\dots ,n 1,2,…,n中选择 m m m个数的所有排列。
输入格式
一行包含两个整数 n , m n,m n,m,两个整数之间用一个空格分开。
输出格式
按字典序输出所有可能的排列,每个排列输出一行,元素之间用一个空格分开。
样例输入
3 2
样例输出
1 2
1 3
2 1
2 3
3 1
3 2
数据范围与提示
1 ≤ m ≤ n ≤ 11 1\le m \le n \le 11 1≤m≤n≤11
#include <iostream>
#include <algorithm>
using namespace std;
int visit[12];//标记访问
int num[12];//标记存储
int n,m;//n的m排列
void DFS(int k){
if(k==m+1){
//输出当前方案
for(int i=1;i<=m;i++){
cout<<num[i]<<' ';
}
cout<<endl;
return;
}
for(int i=1;i<=n;i++){
//查找可选值
if(!visit[i]){
//可选
visit[i]=1;//标记已选
num[k]=i;//记录已选
DFS(k+1);//搜索下一层
visit[i]=0;//回退,标记未选
}
}
}
int main(){
cin>>n>>m;
fill(visit,visit+12,0);//访问标0
DFS(1);//调用回溯
return 0;
}
工作分配
题目描述
有 n n n项工作要分配给 m m m个人完成,每个人只能从事一项工作,且每项工作只能由一人完成。已知第 i i i个人完成第 j j j项工作的工费是 c [ i ] [ j ] c[i][j] c[i][j]元,那么怎么给每个人分配工作才能使得总工费最小。
输入格式
一个整数 n n

回溯算法是一种通过试探来解决问题的算法,它尝试分步的做出选择,如果在某一步发现错误,则退回一步重新选择,直至找到所有可能的解决方案。本文详细介绍了回溯算法的概念、框架,并给出了包括打靶问题、全排列、工作分配、回文数、0/1背包、放棋子、数的划分、n皇后问题、迷宫问题和配对问题在内的多个应用实例,帮助读者深入理解回溯算法的原理和应用。

2607

被折叠的 条评论
为什么被折叠?



