DFS 深度优先搜索
dfs就是从起点出发,走过的要做标记, 从没有标记的点中随意挑一 个往前走,走不了就回退,此种路径搜索策略就称为“深度
优先搜索”,简称“深搜”。就是典型的不撞南墙不回头。
其实dfs就是逻辑的建立的过程 比如说选择一个物品后我们进行下一个物品的选择,但是我们也可以不选择的这个物品然后继续下一个物品的选择,所以有个恢复现场的步骤。
有关DFS 的习题
习题连接:https://vjudge.net/contest/385599
A - Oil Deposits
dfs一定要进行标记,还有注意判断条件发,防止越界
code:
#include<iostream>
#include<cstring>
using namespace std;
const int N=120;
int n,m;
char ma[N][N];
bool vis[N][N];
int to[8][2]={1,0,1,1,0,1,-1,1,-1,0,-1,-1,0,-1,1,-1};
bool check(int x,int y){
if(x>=0&&x<n&&y>=0&&y<m&&!vis[x][y]&&ma[x][y]=='@') return true;
return false;
}
void dfs(int x,int y){
vis[x][y]=1;
for(int i=0;i<8;i++){
int dx=x+to[i][0];
int dy=y+to[i][1];
if(check(dx,dy)){
vis[dx][dy]=1;
dfs(dx,dy);
}
}
}
int main()
{
while(~scanf("%d%d",&n,&m)&&n){
memset(vis,0,sizeof vis);
for(int i=0;i<n;i++) scanf("%s",ma[i]);
int ans=0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
if(ma[i][j]=='@'&&!vis[i][j]){
dfs(i,j);
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}
H - 棋盘问题
思路:逐行开始选择,每次选择之后看是否达到条件,达到条件ans++ ,本行已经选完了,进行到下一行的选择,还可以恢复现场,不选这一行,对下一行就行选择。
code:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
char ma[10][10];
bool vis[10];
int n,k;
int ans;//方案数
void dfs(int row,int num){
if(row==n) return;
for(int j=0;j<n;j++){
if(ma[row][j]=='#'&&!vis[j]){
vis[j]=1;
if(num+1==k){//达到条件就ans++
ans++;
}
dfs(row+1,num+1);//本行已经选择完毕 num+1
vis[j]=0;//恢复现场 不选这一列了
}
}
dfs(row+1,num);//不选这一行
}
int main()
{
while(~scanf("%d%d",&n,&k)&&(n!=-1||k!=-1)){
memset(vis,0,sizeof vis);
for(int i=0;i<n;i++) scanf("%s",ma[i]);
ans=0;
dfs(0,0);
printf("%d\n",ans);
}
return 0;
}
或则是这个代码,下面这个代码对达到已经安排了k个棋子之后的情况不在搜索,所以跟快,但是这个题的数据比较小所以显示不出来
code:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
char ma[10][10];
bool vis[10];
int n,k;
int ans;//方案数
void dfs(int row,int num){
if(num==k){
ans++;
return;
}
if(row==n) return;
for(int j=0;j<n;j++){
if(ma[row][j]=='#'&&!vis[j]){
vis[j]=1;
dfs(row+1,num+1);//本行已经选择完毕 num+1
vis[j]=0;//恢复现场 不选这一列了
}
}
dfs(row+1,num);//不选这一行
}
int main()
{
while(~scanf("%d%d",&n,&k)&&(n!=-1||k!=-1)){
memset(vis,0,sizeof vis);
for(int i=0;i<n;i++) scanf("%s",ma[i]);
ans=0;
dfs(0,0);
printf("%d\n",ans);
}
return 0;
}
F - N皇后问题
思路:跟棋盘问题差不多就是多了个条件
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int N=11;
int col[N];
int n;
int ans[N];
int cnt=0;
bool check(int x){
for(int i=1;i<x;i++){//要考虑到x行之前所有选择的列
if(col[i]==col[x]||abs(col[x]-col[i])==abs(x-i)) return false;
}
return true;
}
// 第一种
//void dfs(int row,int num){
// if(num==n){
// cnt++;
// return ;
// }
// if(row>n) return;
// for(int j=1;j<=n;j++){
// col[row]=j;
// if(check(row)){
// dfs(row+1,num+1);
// col[row]=0;
// }
// }
// dfs(row+1,num);//没有这种不选的情况,因为每一行必须有一个皇后
//}
//第二种
void dfs(int row){
if(row>n){
cnt++;
return ;
}
for(int j=1;j<=n;j++){
col[row]=j;//保存皇后所在行的列
if(check(row)){
dfs(row+1);
col[row]=0;//其实也可以不用恢复现场 ,因为这一行肯定有皇后的
}
}
}
int main()
{
for(int i=1;i<=10;i++){
cnt=0;
n=i;
dfs(1);
ans[n]=cnt;
}
while(~scanf("%d",&n)&&n){
printf("%d\n",ans[n]);
}
return 0;
}
D - 逃离迷宫
思路:只要最小的转弯次数小于k 就输出yes,所以在dfs 的时候需要剪枝,剪去大于最小次数的所有次数
code:
#include<iostream>
#include<cstdio>
#include<cmath>
#define INF 0x3f3f3f3f
using namespace std;
char ma[110][110];
int MIN[110][110];
int to[4][2]={1,0,0,1,-1,0,0,-1};
bool flag;
int x1,y1,x2,y2,k;
int n,m;
bool check(int x ,int y,int temp){
if(x>=0&&x<n&&y>=0&&y<m&&ma[x][y]=='.'&&temp<=MIN[x][y]) return true;
return false;
}
void dfs(int x,int y,int pre,int cnt){
// cout<<"***"<<endl;
if(cnt>k) return ;//大于k次就不用在dfs了
if(x==x2&&y==y2){
flag=1;
return ;
}
for(int i=0;i<4;i++){
int dx=x+to[i][0];
int dy=y+to[i][1];
int temp=cnt;
if(i!=pre) temp+=1;
if(check(dx,dy,temp)){
MIN[dx][dy]=temp;
dfs(dx,dy,i,temp);
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
flag=0;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) scanf("%s",ma[i]);
scanf("%d%d%d%d%d",&k,&y1,&x1,&y2,&x2);
y1--,x1--,y2--,x2--;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
MIN[i][j]=INF;
MIN[x1][y1]=0;
dfs(x1,y1,-1,-1);
if(flag) printf("yes\n");
else printf("no\n");
}
}