图的深度优先搜索经典例题——寻找最大油田

政府现勘探到一片油田,在这一片油田中有很多散落的石油资源。因为经费原因,政府只能开采一处油田,所以需找到最大的油田进行施工。油田的地理情况被简化成了一个矩阵,其中每一个方格代表一块土地,0 代表陆地,1 代表石油资源。如果一处石油资源和另一处石油相连接,则其算一块油田。现要找到最大的相互连接的石油资源,并输出它的面积。

图 1 所示就是一个例子,其中灰色的区域都是不同大小的油田。
油矿区域地理示意图
图 2:油矿区域地理示意图

那么对于这个例子来说,左上角的五块石油连在一起的区域就是最大的油田,其面积为 5,如图 2 所示。
最大面积油田示意图
图 2:最大面积油田示意图

首先,我们分析一下这个问题。问题是找到最大的油田,所以需把每个岛屿的面积算出来,然后比较,在其中找出最大的即可。

为了知道一块油田有多大,我们可能需要遍历图中的每一个方格。不过我们知道,在一块油田中,石油资源一定是相邻的,因此只有四种情况:上、下、左、右。所以结合深度优先遍历算法,我们可以对每一个方格进行搜索来寻找该方格相邻处是否还有石油资源。

但要注意的是,搜索应不重不漏,所以已经搜索过的地方应被标记,以免被重复搜到。以下即为搜索前的处理代码:
#深度优先代码
def MaxAreaOfIsland(grid):   #grid为题目给的二维数组,其中存储着地理信息
    row = len(grid)      #row记录二维数组的行数,也是地图的y轴长度
    row = len(grid)      #row记录二维数组的行数,也是地图的y轴长度
    col = len(grid[0])     #col记录二维数组的列数,也是地图的x轴长度
    arrived = [[False for j in range(col)] for i in range(row)]   #arrived为一个二维数组,存储一块土地是否被访问过
    ans = 0
    return ans

现在就可以开始用深度优先遍历算法测算油田的最大面积了。我们可以用循环依次以每一块土地为起点进行搜索。如果该块土地含有石油资源,则继续搜索,反之则继续循环遍历其他土地。

来看一个例子,最先找到的石油资源是被填充为黑色的土地,如图 3 所示。
搜索过程示意图
图 3:搜索过程示意图

接下来我们需要向它的四个方向查找。如果向上,土地就超出地图的边界了,所以搜索无果。如果向左,该区域其实已经被搜索过,且值为 0,没有石油资源。如果向下,发现没有越出地图边界,并且含有石油资源,这时再搜索我们移动到下面的点。

此时来到下面的土地处,还要遵循一样的顺序,先尝试向上走,发现上面为已经访问过的陆地,继而向左搜索,发现左处不含有石油资源,再尝试向右搜索,搜索到了石油资源,并以右处的方格继续开始新的搜索。如此反复,即可将整幅图搜索完,求解出答案。

为此,我们重新定义一个新的函数来进行深度优先遍历算法,在避免越界和重复访问的条件下,向四个不同的方向递归调用搜索代码如下:
#深度优先代码
def DFS(x, y,grid):
    row = len(grid)    # row记录二维数组的行数,也是地图的y轴长度
    col = len(grid[0])   # col记录二维数组的列数,也是地图的x轴长度
    arrived = [[False for j in range(col)] for i in range(row)]     # arrived为一个二维数组,存储一块土地是否被访问过
    ans = 0
    if x >= 0 and x < row and y >= 0 and y < col and not arrived[x][y] and grid[x] [y] == 1:
        arrived[x][y] = True
        return 1 + DFS(x - 1, y) + DFS(x + 1, y) + DFS(x, y - 1) + DFS(x, y + 1)
    else:
        return 0

最后在主函数中调用这个深度优先遍历函数即可。

综合以上分析,合并成完整代码:
#最大的油田代码
def MaxAreaOfIsland(grid):   #grid为题目给的二维数组,其中存储着地理信息
    row = len(grid)      #row记录二维数组的行数,也是地图的y轴长度
    col = len(grid[0])     #col记录二维数组的列数,也是地图的x轴长度
    arrived = [[False for j in range(col)] for i in range(row)]  #arrived为一个二维数组,存储一块土地是否被访问过
    ans = 0          #记录油田的最大面积
    def DFS(x, y):
        if x >= 0 and x < row and y >= 0 and y < col and not arrived[x][y] and grid[x] [y] == 1:             #判断现在搜索的土地是否出界,是否已经访问过,以及是否含有石油资源
            arrived[x][y] = True #标记该块土地已经被搜索过
            return 1 + DFS(x - 1, y) + DFS(x + 1, y) + DFS(x, y - 1) + DFS(x, y + 1)  #搜索其相邻的土地并将答案加上1
        else:
            return 0
for i in range(row):
    for j in range(col):
        area = DFS(i, j)   #遍历搜索每一块土地
        if area > ans:
            ans = area
return ans