少儿Python每日一题(25):岛屿问题

本文介绍了如何使用Python解决蓝桥杯竞赛中的岛屿问题,这是一个基于深度优先搜索(DFS)的算法应用。文章通过实例详细解释了DFS算法的思路,并探讨了在青少年编程中应用DFS的挑战。此外,还提到了另一个使用DFS解决的典型问题——单词搜索,同样给出了解题策略。

原题解答

本次的题目如下所示(题目来源:蓝桥杯):

编程实现:

有一片海域划分为N*M个方格,其中有些海域已被污染(用0表示),有些海域没被污染(用1表示)。请问这片N*M海域中有几块是没被污染的独立海域(没被污染的独立海域是指该块海域上下左右被已污染的海域包围,且N*M以外的海域都为已被污染的海域)例如:N=4,M=5,4*5的海域中,已被污染海域和没被污染的海域如下图:

这块4*5的海域,有3块海域(绿色)没被污染,因为每一块的上下左右都被污染的海域包围。

输入描述

第一行输入两个正整数N和M,N表示矩阵方格的行,M表示矩阵方格的列,N和M之间以一个英文逗号隔开

第二行开始输入N行,每行M个数字(数字只能为1或者0,1表示没被污染的海域,0表示已被污染的海域)

输出描述

输出一个整数,表示N*M的海域中有几块是没被污染的独立海域

样例输入

4,5

1,1,0,0,0

1,0,1,0,0

1,0,0,0,0

1,1,0,1,1

样例输出

3

本道题使用的是被污染的水域和未被污染的水域为背景,题目的原型是一个经典的算法问题:岛屿问题。即连在一起的是一片岛屿,计算岛屿的个数。

这道题需要用到深度优先搜索算法(DFS),该算法与上一讲中的广度优先搜索算法(BFS)相对应,也是搜索方法之一。DFS算法的特点是从起点开始,沿着可走的路径一路走下去,一直走到无路可走(即不撞南墙不回头)。

这道题我们可以从一个点出发,搜索其上下左右,只要有路就走下去。并将自己和相邻的1都改为0,遍历整个矩阵,遇到1计数加1,并将自己和可连接的点都改为0。这样就可以得到未被污染的水域的个数。

我们先写出代码的主体部分:

n, m = map(int, input().split(','))
water = []
for _ in range(n):
    water.append(list(map(int, input().split(','))))
ans = 0

for i in range(n):
    for j in range(m):
        if water[i][j] == 1:
            # 将自己和能够接壤的1都改为0
            ans += 1
print(ans)

 下面关键部分就是处理注释行部分的内容,深度优先搜索算法是“不撞南墙不回头”,即有路可走就压如栈中,我们可以使用函数的递归实现:

def dfs(x, y, area):
    area[x][y] = 0
    dx = [0, 0, -1, 1]
    dy = [-1, 1, 0, 0]
    for i in range(4):
        nx = x + dx[i]
        ny = y + dy[i]
        if 0 <= nx < len(area) and 0 <= ny < len(area[0]):
            if area[nx][ny] == 1:
                dfs(nx, ny, area)

最后,再注释行的位置调用该函数,即可实现我们想要的功能,完整的代码如下:

def dfs(x, y, area):
    area[x][y] = 0
    dx = [0, 0, -1, 1]
    dy = [-1, 1, 0, 0]
    for i in range(4):
        nx = x + dx[i]
        ny = y + dy[i]
        if 0 <= nx < len(area) and 0 <= ny < len(area[0]):
            if area[nx][ny] == 1:
                dfs(nx, ny, area)


n, m = map(int, input().split(','))
water = []
for _ in range(n):
    water.append(list(map(int, input().split(','))))
ans = 0

for i in range(n):
    for j in range(m):
        if water[i][j] == 1:
            dfs(i, j, water)
            ans += 1
print(ans)

本题拓展

本题考查了深度优先搜索算法,题目难度:★★★★★★

本题的难度在于青少年编程没有系统学习大学的离散数学,缺少图论的知识体系。这里尽量使用最平时的语言解释DFS算法的思想。

DFS的基本思路是向各个方向寻找,只要有路就走,一直走到无路可走为止。一般情况下使用函数的递归调用来实现一路走下去。

除了岛屿问题外,常见的使用DFS算法解决的问题还有单词问题,题目如下(题目来源:Leetcode):

Given an m x n grid of characters board and a string word, return true if word exists in the grid.

The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once.

Example 1:

Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
Output: true

Example 2:

Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
Output: true

Example 3:

Input: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"
Output: false 

题目的大意为:

给定一个 m x n 的字符板网格和一个字符串单词,如果网格中存在单词,则返回 true。
该词可以由顺序相邻单元格的字母构成,其中相邻单元格是水平或垂直相邻的。同一字母单元格不得使用多次。

这道题我们同样使用DFS算法解决,我们从某一个字符出发,如果第一个字母与单词的第一个字母相符,则从上下左右四个方向寻找下一个字母。直到符合单词的长度,且每个字母都相同。这里我们需要注意的是,我们不能走回头路搜索,因此我们在搜索完一个格子时,需要把这个格子中中字母临时做标注,遍历完回溯时再将字母改回来。

这道题的代码可以这样写:

def match(matrix, word, i, j, k):
  # 如果已经匹配完整个单词,返回True
  if k == len(word):
    return True
  # 如果位置越界或者字母不匹配,返回False
  if i < 0 or i >= len(matrix) or j < 0 or j >= len(matrix[0]) or matrix[i][j] != word[k]:
    return False
  # 用一个临时变量保存当前位置的字母,然后将其标记为已访问
  temp = matrix[i][j]
  matrix[i][j] = "#"
  # 在四个方向上递归地搜索下一个字母
  res = match(matrix, word, i-1, j, k+1) or match(matrix, word, i+1, j, k+1) or match(matrix, word, i, j-1, k+1) or match(matrix, word, i, j+1, k+1)
  # 恢复当前位置的字母
  matrix[i][j] = temp
  # 返回结果
  return res


# 测试代码
board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]]
word = input()
flag = False
for i in range(board):
    for j in range(board[0]):
        if match(matrix, word, i, j, 0):
            flag = True
            break
print(flag)


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凤城老人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值