一些搜索总结

双向BFS

我们在BFS时,要从起点,搜遍整颗搜索树,然后找到终点,整个过程大概是一个如下的结构

我比较懒,所以图非常简陋。

最上面的节点表示起点,最下面的点表示终点。

我们正常的BFS是从起点一路向下搜到终点,这样要扩展不少的状态,耗费不少的时间。

这样我们想我们其实可以从起点和终点同时开始搜,起点和终点同时向外扩展一步,当两个搜索路径交会时,结束搜索。

这样我们可以省去一半的时间。

我们用两个队列分别表示起点的扩展和终点的扩展

例题:

常见的迷宫问题,0表示有障碍,1表示无障碍,假设没有无法走到的情况,求最少需要多少步

非常显然的BFS

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 3030;
 4 struct node {
 5     int x, y;
 6 }q1[maxn], q2[maxn];
 7 //int stx, sty, edx, edy;
 8 int a[maxn][maxn];
 9 int n, m;
10 int    tail1 = 0, head1 = 1, tail2 = 0, head2 = 1;
11 //int dx[9] = {-1, -1, 1, 1, -2, -2, 2, 2};
12 //int dy[9] = {2, -2, 2, -2, -1, 1, -1, 1};
13 //int vis[maxn][maxn];
14 int dx[5] = {-1, 0, 1, 0}, dy[5] = {0, -1, 0, 1};
15 int st[maxn][maxn], ed[maxn][maxn];
16 int stept[maxn][maxn], stepe[maxn][maxn];
17 bool flag = 0;
18 int ans = 0;
19 
20 inline int read() {
21     int x = 0, y = 1;
22     char ch = getchar();
23     while(!isdigit(ch)) {
24         if(ch == '-') y = -1;
25         ch = getchar();
26     }
27     while(isdigit(ch)) {
28         x = (x << 1) + (x << 3) + ch - '0';
29         ch = getchar();
30     }
31     return x * y;
32 }
33 
34 int main() {
35     memset(st, 0, sizeof(st));
36     memset(ed, 0, sizeof(ed));
37     memset(a, -1, sizeof(a)); 
38     n = read(), m = read();
39     for(int i = 1; i <= n; ++i)
40         for(int j = 1; j <= m;++j)
41             a[i][j] = read();
42     q1[++tail1].x = 1, q1[tail1].y = 1;
43     q2[++tail2].x = n, q2[tail2].y = m;
44     for(head1 = 1, head2 = 1; head1 <= tail1 || head2 <= tail2; head1++, head2++) {
45         for(int i = 0; i < 4; ++i) {
46             int xx = q1[head1].x + dx[i];
47             int yy = q1[head1].y + dy[i];
48             if(a[xx][yy] == -1) continue;
49             if(!st[xx][yy] && a[xx][yy]) {
50                 stept[xx][yy] = stept[q1[head1].x][q1[head1].y] + 1;
51                 if(ed[xx][yy]) {
52                     flag = 1;
53                     ans = stept[xx][yy] + stepe[xx][yy];
54                     break;
55                 }
56                 st[xx][yy] = 1;
57                 q1[++tail1].x = xx;
58                 q1[tail1].y = yy;
59             }
60         }
61         if(flag == 1) break;
62         for(int i = 0; i < 4; ++i) {
63             int xx = q2[head2].x + dx[i];
64             int yy = q2[head2].y + dy[i];
65             if(a[xx][yy] == -1) continue;
66             if(!ed[xx][yy] && a[xx][yy]) {
67                 stepe[xx][yy] = stepe[q2[head2].x][q2[head2].y] + 1;
68                 if(st[xx][yy]) {
69                     flag = 1;
70                     ans = stepe[xx][yy] + stept[xx][yy];
71                     break;
72                 }
73                 ed[xx][yy] = 1;
74                 q2[++tail2].x = xx;
75                 q2[tail2].y = yy;
76             }
77         }
78         if(flag == 1) break;
79     }
80     cout << ans << '\n';
81     return 0;
82 }
View Code

迭代加深搜索

在一些需要搜索的情况下,使用DFS会因为搜索树的深度太深,加上结果比较靠后,而导致耗费巨大的时间,而如果使用BFS,则会因为要存储大量状态而耗费大量的空间

这个时候我们可以使用迭代加深搜索。

简而言之就是我们人为规定一个搜索深度,每次到限定的搜索深度时停止。

比如说第一次我们规定搜索深度为1,我们搜索一层,结束

第二次我们规定搜索深度为2,我们搜索两层,结束

.......

这样我们会发现我们绝对会出现大量的重复计算,但是这对于我们直接进行DFS在搜索树上跑所需要的时间来说,是少得多的,这样这个算法就显得优秀了

例题1:

这题我们可以发现直接DFS我们似乎无法下手,我们可以脑洞一下,构造出一颗搜索树(当然我在口胡)

我们可以发现从第一个分叉向下扩展,可能会扩展的非常深都没有找到答案。用BFS我们固然可写,但是会耗费不小的空间。

这样我们就可以迭代加深搜索。

需要注意的是黑白棋是交替下的,而且题面并没有明确说是黑棋先下还是白棋先下

我们需要对黑先和白先分别进行计算

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 6;
 4 const int deep = 1000000000;
 5 int ox1, oy1, ox2, oy2;//分别记录两个空白地带的坐标
 6 char ch[maxn]; 
 7 char a[maxn][maxn];
 8 bool flag = 0;
 9 int dx[5] = {-1, 0, 1, 0}, dy[5] = {0, 1, 0, -1};
10 int ans = 0;
11 
12 inline bool check() {
13     for(int i = 1; i <= 4; ++i) {
14         if(a[1][i] == a[2][i] && a[1][i] == a[3][i] && a[1][i] == a[4][i]) return 1;//判断是否存在竖着的四子连棋 
15         if(a[i][1] == a[i][2] && a[i][1] == a[i][3] && a[i][1] == a[i][4]) return 1;//判断是否存在横着的四子连棋 
16     }
17     if(a[1][1] == a[2][2] && a[1][1] == a[3][3] && a[1][1] == a[4][4]) return 1;//判断是否存在由左上到右下角的四子连棋 
18     if(a[1][4] == a[2][3] && a[1][4] == a[3][2] && a[1][4] == a[4][1]) return 1;//判断是否存在由右上到左下角的四子连棋 
19     return 0;
20 }
21 
22 inline bool able(int x, int y, int dad) {
23 return x >= 1 && x <= 4 && y >= 1 && y <= 4 && a[x][y] != dad;}
24 
25 bool deepdfs(int x1, int y1, int x2, int y2, char pre, int begin) {
26     if(begin == ans) {
27         if(check()) return 1;
28         else return 0;
29     }
30     for(int i = 0; i < 4; ++i) {
31         int xx = x1 + dx[i];
32         int yy = y1 + dy[i];
33         if(able(xx, yy, pre)) {
34             swap(a[x1][y1], a[xx][yy]);
35             if(deepdfs(xx, yy, x2, y2, (pre == 'W' ? 'B' : 'W'), begin + 1)) return 1;
36             swap(a[x1][y1], a[xx][yy]);
37         }
38         xx = x2 + dx[i];
39         yy = y2 + dy[i];  
40         if(able(xx, yy, pre)) {
41             swap(a[x2][y2], a[xx][yy]);
42             if(deepdfs(x1, y1, xx, yy, (pre == 'W' ? 'B' : 'W'), begin + 1)) return 1;
43             swap(a[x2][y2], a[xx][yy]);
44         }
45     }
46     return 0;
47 }
48 
49 int main() {
50     //memset(a, -1, sizeof(a));
51     for(int i = 1; i <= 4; ++i) {
52         cin >> ch + 1;
53         for(int j = 1; j <= 4; ++j) {
54             if(ch[j] == 'B') a[i][j] = 'B';
55             else if(ch[j] == 'W') a[i][j] = 'W';
56             if(ch[j] == 'O') {
57                 a[i][j] = 'O';
58                 if(!flag) {ox1 = i, oy1 = j;flag = 1;}
59                 else if(flag) ox2 = i, oy2 = j;
60             }
61         }
62     }
63     /*for(uint i = 1; i <= 4; ++i) {
64         for(uint j = 1; j <= 4; ++j)
65             cout << a[i][j] << ' ';
66         cout << '\n';
67     }*/
68     for(ans = 1; ans <= deep; ++ans) {//迭代加深搜索枚举搜索限定深度 
69         if(deepdfs(ox1, oy1, ox2, oy2, 'B', 0))
70             break;
71         if(deepdfs(ox1, oy1, ox2, oy2, 'W', 0))
72             break;
73     }
74     cout << ans << '\n';
75     return 0;
76 }
View Code

 

posted @ 2018-07-04 19:13  YuWenjue  阅读(280)  评论(0编辑  收藏  举报