最近几天在洛谷上练习搜索类的题目,虽然效率极其低下,但慢慢还是摸索出一些深度及广度搜素的套路。现在来总结几道深度优先搜索的模板题。
1、经典的八皇后问题:
输入:方阵的大小n,
输出:前三行输出前三种方式(每行的皇后的列标),第四行输出方法总数;
思想:以列为单位放置皇后,放置一个皇后之后,标记上此皇后所在行、两条斜线;下一次放置之前则先判断当前所在行、两条斜线有没有被标记;此题需要搞清楚所在两条斜线的坐标特点。右上的斜线上每个点所在横纵坐标之和都相等;左下的斜线横纵坐标之差相等。因此此题需要用三种不同的标记。
题目代码:

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'出现的位置,从此处开始四个方向开始寻找可匹配下一个单词的地方,找到则沿着这条路继续走,找不到则返回到最初的起点,换下一条路,此题不需标记上一步走过的位置,但需要把每一步符合要求的坐标都保存在另一个数组里做上标记,便于最后的输出。
解题代码:

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、单词接龙
解题思路:首先需要找到每个单词之间的最小公共部分(有公共部分的才能连接起来),从第一个单词开始进行深搜,记录每次结果的长度,选择最大长度输出。
代码实现:

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 }
深搜可以记录每一次搜索的结果,适用于解答需答题目要求多种解的类型,在很多题型中深搜也结合记忆化搜索在运用,做题时需要灵活思考,不能死套模板
