1.dfs
深度优先搜索:
常用递归实现,先画出递归树,深度优先指的是按照一条路线一直走到头,然后回溯,在回溯过程中还要检查是否有其他路径,如果有则继续走:
(递归还没学明白的小可爱去看杂文那一篇。。。。)
1.全排列问题:枚举并记录记录每个数字的状态,对已经使用过的数字做标记
int p[N],s[N];
int m;
void dfs(int n){
if(n==m){//终止条件
for(int i=0;i<n;i++) printf("%d ",p[i]);//输出
puts("");
return;
}
for(int i=1;i<=m;i++)//枚举
if(!s[i]){
p[n]=i;//将i赋值给当前位置存储
s[i]=1;//改变状态,
dfs(n+1);//进行下一个位置的查找
s[i]=0;//回溯过程中清除记录;
}
}
2.递归枚举:
#include<iostream>
using namespace std;
int n;
int s[20];
void dfs(int m){
if(m==n){
for(int i=0;i<n;i++)
if(s[i]==1) printf("%d ",i+1);
puts("");
return;
}
s[m]=1;//状态选择,1表示选择
dfs(m+1);
s[m]=0;//0表示没有选择;
s[m]=2;//2表示不选;
dfs(m+1);
s[m]=0;
}
int main(){
cin>>n;
dfs(0);
return 0;
}
回溯:在执行完最后一层递归之后肯定会进行回溯(可以用栈的思想来理解),回溯过程中就要清楚记录
部分和问题
输入一个数字n,然后输入n个数字和一个数字k,在数组中挑选若干个数字,使得总和刚好是k;
状态分析 dfs(n,sum)在前n个中选择,和为sum;
#include<iostream>
using namespace std;
int a[25];
int n,k;
bool dfs(int m,int sum){
if(m==n) return sum==k;//所有数字已经选完(不一定全部都选,分选与不选的情况),判断和是不是等于k
if(dfs(m+1,sum) or dfs(m+1,sum+a[m])) return true;//可以理解到为执行完不选择之后
//假如为false,返回到上一层,然后看选择这个数字的方案。
return false;
}
int main(){
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
cin>>k;
if(dfs(0,0)) puts("YES");
else puts("NO");
return 0;
}
n皇后问题:题目不做叙述;
解法一(全排列解法):
在n*n的棋盘上,每一列有一个皇后(对应y轴),然后只需要判断对应x轴是否已经选数字和对应的两条对角线上是否有数字即可;
#include<bits/stdc++.h>
using namespace std;
const int N=20;
int n;
char g[N][N];//棋盘;
bool a[N],b[N],c[N];
void dfs(int u){//u表示列;
if(u==n){
for(int i=0;i<n;i++) puts(g[i]);
puts("");
}
for(int i=0;i<n;i++)
if(!a[i] and !b[u+i] and !c[n+u-i]){//考虑到u-i可能成负数的情况;
g[u][i]='Q';//数组b:y=-x+b,//数组c:y=x+b;数组a表示行;
a[i]=b[u+i]=c[n+u-i]=true;
dfs(u+1);
a[i]=b[u+i]=c[n+u-i]=false;//回溯过程;
g[u][i]='.';
}
}
int main(){
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
g[i][j]='.';
dfs(0);
return 0;
}
解法二:枚举每个格子,直到枚举到n^2;
#include<bits/stdc++.h>
using namespace std;
int n;
const int N=20;
bool row[N],col[N],dg[N],udg[N];
char g[N][N];
void dfs(int x,int y,int s){//x,y表示行和列,s表示皇后个数
if(x==n) x=0,y++;//当x=n时表示一行已经枚举完成,直接跳到下一行
if(y==n){//y=n表示已经全部枚举完;
if(s==n){
for(int i=0;i<n;i++) puts(g[i]);
puts("");
}
return;
}
dfs(x+1,y,s);//不放皇后的情况
if(!row[y] and !col[x] and !dg[x+y] and !udg[x-y+n]){//放置皇后时需要进行判断,行列对角线都不能有皇后;
g[y][x]='Q';
row[y]=col[x]=dg[x+y]=udg[x-y+n]=true;
dfs(x+1,y,s+1);
row[y]=col[x]=dg[x+y]=udg[x-y+n]=false;//回溯:
g[y][x]='.';
}
}
int main(){
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
g[i][j]='.';
dfs(0,0,0);
return 0;
}
bfs宽度优先搜索
一层一层的走,直到走完,可以和深搜做对比记忆;
走迷宫问题:
当队列不为空,扩展队头;
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int d[N][N],g[N][N];//g用来存储迷宫,d表示每个点到起点的距离;
int n,m;
typedef pair<int,int> pii;//存储坐标;
int bfs(){
memset(d,-1,sizeof d);
queue<pii>a;
a.push({0,0});//第一个入队;
d[0][0]=0;//当前位置距离起点距离为0;
while(!a.empty()){
auto t=a.front();//取出队头;
a.pop();
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};//分别表示上下左右;
//扩展队头;
for(int i=0;i<4;i++){
int x=t.first+dx[i],y=t.second+dy[i];
if(x>=0 and x<n and y>=0 and y<m and !g[x][y] and d[x][y]==-1){//x<n,y<m判断边界,!g[x][y]表示路径,d[x][y]表示未走过的点;
d[x][y]=d[t.first][t.second]+1;//经过这个点;
a.push({x,y});
}
}
}
return d[n-1][m-1];
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>g[i][j];
cout<<bfs()<<endl;
return 0;
}