前言:老师讲dfs的时候肯定会提到8皇后问题,这是一个经典的组合问题,虽然是老题目了,但小白拿来练习还是很合适的。这篇再来分享一些dfs的题目,然后就开始新内容啦
题目:N皇后问题 N皇后问题
思路:
-
棋盘表示:用二维数组表示棋盘,1 表示放置皇后,0 表示空位。
-
DFS 递归:从第 1 行开始,逐行尝试放置皇后,递归处理下一行。
-
检查函数:判断当前位置是否安全(同行、同列、对角线无皇后)。
-
回溯:递归返回后,撤销当前放置,尝试下一个位置。
-
计数:如果成功放置 n 个皇后,计数器加一。
#include<iostream>
using namespace std;
int n,cnt;
int a[15][15];
bool check(int x,int y){
int i,j;
for(i=1;i<=n;i++){
if(a[i][y]) return 0; //同列有皇后
if(a[x][i]) return 0; //同行有皇后
}
for(i=x,j=y;i>=1&&j>=1;i--,j--){ //因为是从上往下放置,只要检查左上角和右上角有无皇后
if(a[i][j]) return 0; //主对角线检查
}
for(i=x,j=y;i>=1&&j<=n;i--,j++){ //副对角线检查
if(a[i][j]) return 0;
}
return 1;
}
void dfs(int index){
if(index==n+1){ //成功放置了n个,计数
cnt++;
return;
}
for(int i=1;i<=n;i++){
if(check(index,i)){ //如果安全则放置皇后
a[index][i]=1; //标记
dfs(index+1); //放置下一个
a[index][i]=0; //回溯
}
}
}
int main(){
cin>>n;
dfs(1); //从第一行开始放置皇后
cout<<cnt;
return 0;
}
写了这么多题,感觉dfs很重要的一点就是回溯呀! 是的,但也不全是。我无意识的都会回溯,wyh的迷宫 这题我+了回溯就超时了,数据量有500,每一步都返回时间复杂度自然高,而这题只是问能不能到达,那就一直搜索,不要重新访问节点,看看能不能到终点就行了/_\,我们在套路的同时也要灵活一点,特别注意数据大小。直接放代码了(学了后面的bfs,可以返回来用bfs做做)
#include<iostream>
using namespace std;
int n,m,sx,sy,flag;
char a[510][510];
void dfs(int x,int y){
if(a[x][y]=='t'){
flag=1;
return;
}
if(x<1||x>n||y<1||y>m||a[x][y]=='x') return;
a[x][y]='x';
dfs(x-1,y);
dfs(x+1,y);
dfs(x,y-1);
dfs(x,y+1);
//a[x][y]='.'; 不需要回溯
}
int main(){
int t,i,j;
cin>>t;
while(t--){
flag=0;
cin>>n>>m;
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
cin>>a[i][j];
if(a[i][j]=='s') sx=i,sy=j;
}
}
dfs(sx,sy);
if(flag) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
需要恢复状态(回溯)的情况:找出所有可能的解;组合/排列问题(需要尝试多种选择)
不需要恢复状态的情况:只需要找到一个解;路径是唯一的,不需要尝试多种可能性(如上一篇的水池数目)
这块就先分享这么多一些典型的题目了,搜索还可以和数论啊什么的结合,复杂起来题目老难了(对于目前的我来说),这几篇文章就只是分享一些基础的思维,谢谢阅读~