什么是回溯法:
从问题某一种可能情况出发,搜索所有能到达的可能情况,然后以其中的一种情况为出发点,继续向下探求,走出一条“路”。当一条路走到尽头却仍无目标时倒回上一出发点,从另一可能情况出发继续搜索。
回溯法的思想:
栈(后进先出),穷举搜索与函数递归
回溯法的步骤:
代码如下:
void find(int k){
if(k>n) print;
for满足约束条件的情况{
记录状态;
添加标志;
find(k+1);
删除标志;
}
}
例题1:全排列问题(洛谷P1706:#1236)
问题分析:有眼睛得:从根节点0出发,每次生成n个子节点,递归边界即为n
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,a[10];
void search(int k){
if(k>n){
for(int i=1;i<n;i++) printf("%d ",a[i]);
printf("%d\n",a[n]);
return;
}
for(int i=1;i<=n;i++){
bool flag=true; //标记该节点
for(int j=1;j<k&&flag;j++){
if(i==a[j]) flag=0;
}
if(flag){
a[k]=i;
search(k+1); //搜索对应子节点
}
}
}
int main(){
scanf("%d",&n);
search(1); //从1搜索
return 0;
}
例题2:N皇后问题(简化洛谷P1219:#1237)
问题分析:有眼睛得:每次更新整张棋盘复杂度过高,容易TLE,所以用三个一维数组分别替代列、左下斜、右下斜。由于要求输出总数,所以用ans++来记录。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,ans;
bool f1[20],f2[20],f3[20];
void find(int k){
if(k>n){
ans++;
return; //递归边界
}
for(int i=1;i<=n;i++){
if(!f1[i]&&!f2[i+k-1]&&!f3[n-k+i]){ //推导同一斜线上各点横纵坐标和差关系
f1[i]=f2[i+k-1]=f3[n-k+i]=1; //标记点
find(k+1);
f1[i]=f2[i+k-1]=f3[n-k+i]=0; //删除标记
}
}
}
int main(){
cin>>n;
memset(f1,0,sizeof(f1)); //初始化,如果是全局变量可省
memset(f2,0,sizeof(f2));
memset(f3,0,sizeof(f3));
find(1);
cout<<ans;
return 0;
}