上一次我经历了我与深搜的第一次。理解了之后感觉很有成就感。
但是,那仅仅是一部分。我对深搜还没有完全的理解。那么就下来,就再来一次。
(听说week4的第一题要用广搜而不是深搜,但是我打算先学会深搜。毕竟都是学习嘛。)
(深搜吃透了,广搜其实就好理解了)
那么先把这次研究的例题搞上去:
问题:GeoSurvComp地质调查公司负责探测地下石油储藏。 GeoSurvComp现在在一块矩形区域探测石油,并把这个大区域分成了很多小块。他们通过专业设备,来分析每个小块中是否蕴藏石油。如果这些蕴藏石油的小方格相邻,那么他们被认为是同一油藏的一部分。在这块矩形区域,可能有很多油藏。你的任务是确定有多少不同的油藏。
input: 输入可能有多个矩形区域(即可能有多组测试)。每个矩形区域的起始行包含m和n,表示行和列的数量,
1<=n,m<=100,如果m =0表示输入的结束,接下来是n行,每行m个字符。每个字符对应一个小方格,并且要么是’*’,代表没有油,要么是’@’,表示有油。
output: 对于每一个矩形区域,输出油藏的数量。两个小方格是相邻的,当且仅当他们水平或者垂直或者对角线相邻(即8个方向)。
这个问题与之前的那个稍微复杂了一些。但是也不成问题。
上一个问题中,我们达到下一行之后,要先开始一个for循环。去遍历我们在这一行里面的所有情况。
我们抽象一下。再上一个题里面。我们每走一步就是进到下一个行。也就是说,我们的“步”就是行。
这个题呢?我们的每一步,就成了每一格油田了,我们的下一步是下一格油田。
然后呢?每一步之间是什么联系的呢?
上一个题目,当前步要先进行数字的选择才能进到下一步。这里也一样,这里是先要进行方向的选择,才能进到下一步。
二者有什么不同呢?
他们其实没什么不同。上一个的第一次调用是去找所有的组合,这次一的调用失去开辟每一个方向。其实二者都是不断地产生分支。但是这分支绝对不是同时产生的!如果用for循环所给的‘i“来表示我们的分支的话:
第一次:11111111111
第二次:11111111112
第三次:11111111121
就是这样。
原因就在于多个递归的出栈顺序是先进先出的。
okk
那么下面分析一下代码。
分析的话,我就放在代码里面的注释里了!
#include <iostream>
using namespace std;
int m, n;
int sum = 0;
int judge(int x, int y);
int dfs(int x, int y);
int a[101][101];
int v[9][2] = { {0,0},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1.0},{-1,1} };
int main()
{
cin >> m >> n;
for (int i = 1; i <= m; i += 1)
{
for (int x = 1; x <= n; i += 1)
{
cin >> a[m][n];
}
}
// m行,n列
for (int x = 1; x <= m; x += 1)
{
for (int y = 1; y <= n; y += 1)
{
if (dfs(x, y))
{
sum += 1;
}
}
}
cout << sum;
return 0;
}
int judge(int x, int y)
{
if (a[x][y] == '@'&&x<=100&&y<100) // 这里用来形成油田的边界
{
return 1;
}
else
{
return 0;
}
}
int dfs(int x, int y)
{
if (judge(x, y)) // 注意,我们的深搜的程序必须在这个if里面,因为我们只要碰到不是油田的,就停止这一个分支
{
a[x][y] = '*'; // 以此来标记一个已经被统计过的油田
// 上面这一步很重要这种格子地图遍历的关键
// 走迷宫也是,我们走过了就不能再走了,这时候就要标记。
// 大家想一下,为什么迷宫题走过了就不能再走了?原因在于避免重复统计
// 下面想一下怎么去解决遍历方向的问题
// 一共八个方向,很自然的就像想到,可以用一个for循环实现。也就是i从1到8;
// 然后我想到了矩阵。刚学的。
// 我们建立一个二维数组,代表了向8个方向走的话,x与y的变化
// 就叫这个数组v吧。
for (int i = 1; i <= 8; i += 1)
{
// 这时候我遇到了一个问题,那就是这个新的坐标应该定义在什么地方?
// 想一想,这个新的坐标只有接下来的调用需要用,所以在for循环小节里面定义就好
int nx, ny;
nx = x + v[i][0];
ny = y + v[i][1];
// 选择之后进行递归的调用
// 不要脸地把任务交给无数个dfs
dfs(nx, ny);
return 1; // 这就是深搜地精华!这是程序一脚踏进去深不见底的调用以后,终于爬出来以后的样子
}
}
return 0; // 就像前面说的一样,如果这个起步的点本身就不是油田的话,那就直接理都不理,sum也不会+1
}
okk
就到这里啦