dfs的搜索是基于栈,但一般可以用用递归实现,实际上用的是系统栈。有内部搜索和外部搜索两种,内部搜索是在图的内部,内部搜索一般基于连通性,从一个点转移到另一个点,或者判断是否连通之类的问题,只需要标记避免重复访问即可,不需要还原状态,而外部搜索则是将一张图视为一种状态,每一个状态延伸得到新的状态,要保证每个状态往外延伸时都是相同的,那么就需要还原状态。
搜索顺序也很重要,我们要找到合适的搜索顺序保证不漏的搜出每一种情况,重复当然没什么问题。
另外,同宽搜相比,深搜的代码会简单一些,但是却有爆栈的风险,递归层数比较高的时候需要注意。
1112. 迷宫(活动 - AcWing)
思路:本质上就是判断能不能从这点到达另一点,那么就是连通性问题,深搜宽搜都可以,这里我们用深搜来实现。
#include<bits/stdc++.h>
using namespace std;
int n;
char s[120][120];
int sx,sy,ex,ey;
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int st[120][120];
int dfs(int x,int y)
{if(s[x][y]=='#') return 0; if(x==ex&&y==ey) return 1;st[x][y]=1;for(int i=0;i<4;i++){int nx=x+dx[i],ny=y+dy[i];if(nx<0||nx>=n||ny<0||ny>=n) continue;if(st[nx][ny]) continue;if(dfs(nx,ny)) return 1;}return 0;
}
int main()
{int t;scanf("%d",&t);while(t--){scanf("%d",&n);for(int i=0;i<n;i++) scanf("%s",s[i]);scanf("%d%d%d%d",&sx,&sy,&ex,&ey);if(dfs(sx,sy)) cout<<"YES"<<endl;else cout<<"NO"<<endl;memset(st,0,sizeof st);}
}
1113. 红与黑(1113. 红与黑 - AcWing题库)
思路:实际上就是统计一个连通块内有多少个元素。
#include<bits/stdc++.h>
using namespace std;
char g[30][30];
int n,m;
int cnt;
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int st[30][30];
void dfs(int x,int y)
{st[x][y]=1;cnt++;for(int i=0;i<4;i++){int nx=x+dx[i],ny=y+dy[i];if(nx<0||nx>=n||ny<0||ny>=m) continue;if(st[nx][ny])continue;if(g[nx][ny]=='#') continue;dfs(nx,ny);}
}
int main()
{while(scanf("%d%d",&m,&n)){if(!n&&!m) break;for(int i=0;i<n;i++) scanf("%s",g[i]);for(int i=0;i<n;i++)for(int j=0;j<m;j++)if(g[i][j]=='@'){cnt=0;dfs(i,j);cout<<cnt<<endl;break;}memset(st,0,sizeof st);}
}
1116. 马走日(活动 - AcWing)
思路:这题看似是说在棋盘内部移动,但实际上每次统计一个结果的前提是整个棋盘都被遍历,而且每一步移动有八个选择(理想情况),八个选择各自代表不同的状态,所以我们需要还原状态,只用在最后递归到所有节点都访问过时,记录一下即可。
#include<bits/stdc++.h>
using namespace std;
int n,m;
int st[10][10];
int ans;
int dx[]={1,1,-1,-1,2,2,-2,-2};
int dy[]={2,-2,2,-2,1,-1,1,-1};
void dfs(int x,int y,int k)
{if(k==n*m) {ans++;return;}st[x][y]=1;for(int i=0;i<8;i++){int nx=x+dx[i],ny=y+dy[i];if(nx<0||nx>=n||ny<0||ny>=m) continue;if(st[nx][ny]) continue;dfs(nx,ny,k+1);}st[x][y]=0;
}
int main()
{int t;scanf("%d",&t);while(t--){int x,y;scanf("%d%d%d%d",&n,&m,&x,&y);ans=0;dfs(x,y,1);cout<<ans<<endl;}
}
ps:特殊情况单独判返回的时候一定要注意状态有没有请干净。
1117. 单词接龙(活动 - AcWing)
思路:这里首先要想搜索,我们需要建立单词与单词之间的关系,准确来说我们需要知道哪些单词可以接在哪些单词的后面,它们的重合长度是多少,这里的重合长度一定要越小越好,因为我们可以任意选择重合长度。
那么预处理完之后,直接深搜记录最大值即可。
另外要注意每个单词只能用两次,那么需要记录一下当前单词已经用了几次了,我们可以用下标来实现。
#include<bits/stdc++.h>
using namespace std;
int n;
string s[30];
int used[30];
int g[30][30];
int mx;
void dfs(string r,int k)
{if(used[k]==2) return;used[k]++;for(int i=0;i<n;i++){if(g[k][i]&&used[i]<2){string tmp=r+s[i].substr(g[k][i]);//cout<<r<<" "<<s[i].substr(g[k][i])<<" "<<g[k][i]<<endl;mx=max(mx,(int)tmp.size());dfs(r+s[i].substr(g[k][i]),i);}}used[k]--;
}
int main()
{scanf("%d",&n);for(int i=0;i<n;i++) cin>>s[i];for(int i=0;i<n;i++){for(int j=0;j<n;j++){for(int k=1;k<min(s[i].size(),s[j].size());k++){if(s[i].substr(s[i].size()-k,k)==s[j].substr(0,k)){g[i][j]=k;break;}}//printf("%d ",g[i][j]);}//printf("\n");}char c;cin>>c;mx=0;for(int i=0;i<n;i++){if(s[i][0]==c){mx=max(mx,(int)s[i].size());dfs(s[i],i);}}cout<<mx;
}
1118. 分成互质组(活动 - AcWing)
思路:如果我们将第一个数放在第一组,那么遍历后面的数,如果不与第一组中的数互质,那么就可以放进这一组,否则就只能新开一组,如此递归来实现,就可保证将所有的数都分好组,同时是分组最少的。
另外由于放进同一个组内的顺序无所谓,所以当不用新开一个组的时候,遍历下一层的时候,直接从后一个元素开始遍历,否则,如果新开组,那么就要从开头开始遍历。因为是按顺序遍历的,所以当前元素被遍历前,它之前的元素与这一组的关系都已经判断过了,所以如果不新开组那么就没必要再往前看。
#include<bits/stdc++.h>
using namespace std;
int n;
int a[20];
int g[20][20];
int ans=10;
int st[20];
int gcd(int a,int b)
{return b?gcd(b,a%b):a;
}
int check(int u,int g[],int cn)
{for(int i=0;i<cn;i++){if(gcd(a[u],a[g[i]])>1) return 0; }return 1;
}
void dfs(int u,int c,int k,int cn)//u是此时从哪个点开始判断,c是当前处在第几个组,k是已经分配几个点了,cn当前组中有多少个元素
{if(c>=ans) return;if(k==n){ans =min(ans,c);return;}int flag=1;for(int i=u;i<n;i++){if(!st[i]&&check(i,g[c],cn))//一个点都不能放入了再新开{st[i]=1;g[c][cn]=i;dfs(u+1,c,k+1,cn+1);st[i]=0;flag=0;}}if(flag) dfs(0,c+1,k,0);
}
int main()
{scanf("%d",&n);for(int i=0;i<n;i++) scanf("%d",&a[i]);dfs(0,1,0,0);cout<<ans;
}