最近几天在洛谷上练习搜索类的题目,虽然效率极其低下,但慢慢还是摸索出一些深度及广度搜素的套路。现在来总结几道深度优先搜索的模板题。
1、经典的八皇后问题:
输入:方阵的大小n,
输出:前三行输出前三种方式(每行的皇后的列标),第四行输出方法总数;
思想:以列为单位放置皇后,放置一个皇后之后,标记上此皇后所在行、两条斜线;下一次放置之前则先判断当前所在行、两条斜线有没有被标记;此题需要搞清楚所在两条斜线的坐标特点。右上的斜线上每个点所在横纵坐标之和都相等;左下的斜线横纵坐标之差相等。因此此题需要用三种不同的标记。
题目代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 bool flag[4][50]={0};//flag[1]代表所在行被标记,flag[2]代表右下斜线,flag[3]标记左上的斜线 4 int n,sum=0;//n代表方阵大小,sum代表解法的总数 5 int ans[50];//保存每次选择的列坐标 6 7 void dfs(int i){ 8 int j; 9 if(i>n){//所有列都访问完即为一种方法 10 ++sum; 11 if(sum>3)return ; 12 else 13 { 14 for(int i=1;i<=n;++i) 15 printf("%d ",ans[i]); 16 printf("\n"); 17 return ; 18 } 19 } 20 for(j=1;j<=n;j++){ 21 if(!flag[1][j] && !flag[2][i+j] && !flag[3][i-j+n]){//所在行、两边的斜线都没有被标记才能选 22 ans[i]=j; 23 flag[1][j]=1; 24 flag[2][i+j]=1; 25 flag[3][i-j+n]=1; 26 dfs(i+1); 27 //回溯,取消标记 28 flag[1][j]=0; 29 flag[2][i+j]=0; 30 flag[3][i-j+n]=0; 31 } 32 } 33 } 34 35 int main(){ 36 cin>>n; 37 dfs(1); 38 cout<<sum; 39 return 0; 40 }
2、单词方阵问题
输入格式
第一行输入一个数nnn。(7≤n≤100)。
第二行开始输入n×n的字母矩阵。
输出格式
突出显示单词“yizhong”的n×n矩阵。
解题思路:找出每一个'y'出现的位置,从此处开始四个方向开始寻找可匹配下一个单词的地方,找到则沿着这条路继续走,找不到则返回到最初的起点,换下一条路,此题不需标记上一步走过的位置,但需要把每一步符合要求的坐标都保存在另一个数组里做上标记,便于最后的输出。
解题代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 struct node{ 4 int x,y;//每个字符所在的横纵坐标 5 }c[120]; 6 7 int n; 8 string st = "yizhong"; 9 char s[120][120]; 10 int visit[120][120]={0}; 11 int next1[][2]={1,0,1,1,1,-1,-1,0,-1,1,-1,-1,0,1,0,-1}; 12 13 void dfs(int x,int y,int k,int cur){ 14 15 if(cur>=7){ 16 for(int i=0;i<7;i++) 17 visit[c[i].x][c[i].y]=1; 18 } 19 20 else { 21 int dx=x+next1[k][0]; 22 int dy=y+next1[k][1]; 23 if(cur==6||s[dx][dy]==st[cur+1] ){ 24 c[cur].x=x;c[cur].y=y; 25 dfs(dx,dy,k,cur+1); 26 27 } 28 } 29 } 30 31 int main(){ 32 cin>>n; 33 for(int i=0;i<n;i++) 34 for(int j=0;j<n;j++) 35 cin>>s[i][j]; 36 37 38 for(int i=0;i<n;i++) 39 for(int j=0;j<n;j++) 40 if(s[i][j]=='y') 41 for(int k=0;k<8;k++){ 42 int x=i+next1[k][0]; 43 int y=j+next1[k][1]; 44 if(s[x][y]=='i') 45 dfs(i,j,k,0); 46 } 47 48 for(int i=0;i<n;i++){ 49 for(int j=0;j<n;j++){ 50 if(visit[i][j]==1)cout<<s[i][j]; 51 else cout<<"*"; 52 } 53 cout<<endl; 54 } 55 return 0; 56 }
结果展示:
3、单词接龙
解题思路:首先需要找到每个单词之间的最小公共部分(有公共部分的才能连接起来),从第一个单词开始进行深搜,记录每次结果的长度,选择最大长度输出。
代码实现:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int aga[25][25]; 4 int n,len=0,mlen=-1; 5 string s[21]; 6 char ch; 7 int visit[25]={0}; 8 9 int check(int i,int j){//找到单词之间的最小公共部分 10 int kj=0; 11 int f=1; 12 for(int k=s[i].size()-1;k>=0;k--){ 13 for(int ki=k;ki<s[i].size();ki++){ 14 if(s[i][ki]!=s[j][kj++]){//第一个字符不匹配 ,前面字符串调到上一个,后面字符串回到第一个 15 f=0; 16 break; 17 } 18 } 19 if(f==1)//匹配成功 20 return s[i].size()-k; 21 kj=0; 22 f=1; 23 } 24 return 0; 25 } 26 27 int dfs(int i)//参数为字符串的序号 28 { 29 30 int f=0; 31 for(int j=0;j<n;j++){ 32 if(visit[j]>=2)continue;//访问次数超过为2 33 if(aga[i][j]<1)continue;//没有重叠部分 34 if(aga[i][j]==s[i].size() || aga[i][j]==s[j].size())continue;//重叠部分包含单词本身的情况 35 f=1; 36 len+=s[j].size()-aga[i][j]; 37 visit[j]++; 38 dfs(j); 39 len-=s[j].size()-aga[i][j];//回溯 40 visit[j]--; 41 } 42 if(f==0)//到达龙 的结尾,保存长度 43 mlen=max(len,mlen);//保存“龙”最长时候的长度; 44 return 0; 45 } 46 int main(){ 47 cin>>n; 48 for(int i=0;i<n;i++){ 49 cin>>s[i]; 50 } 51 cin>>ch; 52 for(int i=0;i<n;i++) 53 for(int j=0;j<n;j++) 54 aga[i][j]=check(i,j); 55 for(int i=0;i<n;i++){ 56 if(s[i][0]==ch&&s[i].size()>1) 57 { 58 visit[i]++; 59 len=s[i].size(); 60 dfs(i); 61 visit[i]=0; 62 } 63 } 64 cout<<mlen; 65 return 0; 66 }
深搜可以记录每一次搜索的结果,适用于解答需答题目要求多种解的类型,在很多题型中深搜也结合记忆化搜索在运用,做题时需要灵活思考,不能死套模板