110.字符串接龙:
思路
首先分析题目,要求是将beginStr转换为endStr,每步只能转换一个字符,且转换后的字符要在字典strList中,最后求最短转换序列长度。
beginStr、endStr和strList中的字符串可以构成一张无向图,判断节点是否连接的依据是,两个节点之间是否只相差一个字符
示例如下图
那么应当采用深搜,需要保存路径长度,同时因为是无向图,所以需要visited判断strList中的节点是否遍历过。
因为会出现endStr不在字典中的情况,因此队列中的操作,应当是,先出队一个节点,再判断该节点与endStr是否存在连线,存在就打印路径长度;否则遍历字典中的字符串,当字符串没有遍历过且存在连线时标记,和入队列
from collections import deque
def judge(str1, str2): # 判断str1和str2之间是否相差一个字符
count = 0
for i in range(len(str1)):
if str1[i] != str2[i]:
count += 1
return (count == 1)
def main():
n = int(input())
beginStr, endStr = input().split()
strList = []
for _ in range(n):
strList.append(input())
if beginStr == endStr: # 开始字符串等于结束字符串
print(1)
return
visited = [False] * n # 标记字典中的单词是否被访问过
que = deque()
que.append([beginStr, 1]) # 队列中记录[字符串, 序列中字符串数量]
while len(que) != 0:
curStr, num = que.popleft()
if judge(curStr, endStr): # 当前字符串与目标字符串是否差一步
print(num + 1)
return
for i in range(n):
if not visited[i] and judge(curStr, strList[i]):
visited[i] = True
que.append([strList[i], num + 1])
print(0) # 不存在这样的序列
return
if __name__ == '__main__':
main()
同时需要注意会出现beginStr == endStr和不存在转换序列的情况,都要进行处理
105.有向图的完全可达性:
思路
分析题目:给定一个有向图,判断从1节点能否到达任何节点,能打印1,不能打印-1。那么从1节点开始搜索整张图,使用visited记录遍历到的节点,最后通过visited判断是否有没有到达的节点
深搜
递归三部曲
- 参数和返回值
参数是图graph, visited数组, x当前节点,没有返回值 - 临界条件
dfs可以分为处理当前节点和处理下一个节点,如果是处理当前节点,那么需要将临界条件独立出来;如果是处理下一个节点,那么临界条件被包含在正常处理情况中
if visited[x]: # 如果遍历过x
return
- 正常递归
- 处理当前节点:
visited[x] = True for i in graph[x]: # 以邻接表为例 dfs(graph, visited, i)
- 处理下一个节点:临界条件在,如果x连接的节点都已经被访问过了,那么会判断完之后会直接返回上一层
- 处理当前节点:
for i in graph[x]: # 以邻接表为例
if not visited[i]: # 没有访问过
dfs(graph, visited, i)
此处没有用到回溯,假设用到了回溯,那么就是visited[i] = False,但是我们要求的visited记录节点1能访问到的所有节点为True,那么就出错了,因此本题的dfs不要求回溯
代码
"""
dfs处理下一个节点
"""
from collections import defaultdict
def dfs(graph, visited, x):
for i in graph[x]:
if not visited[i]:
visited[i] = True
dfs(graph, visited, i)
def main():
n, k = map(int, input().split())
graph = defaultdict(list)
# 邻接表
for _ in range(k):
s, t = map(int, input().split())
graph[s].append(t)
visited = [False] * (n + 1) # 节点编号从1开始
visited[1] = True
dfs(graph, visited, 1)
# 查看是否有不能到达的节点
for i in range(2, n + 1):
if visited[i] == False:
print(-1)
return
print(1) # 可以到达
if __name__ == '__main__':
main()
"""
dfs处理当前节点
"""
from collections import defaultdict
def dfs(graph, visited, x):
if visited[x]:
return
visited[x] = True # 处理当前节点
for i in graph[x]:
dfs(graph, visited, i)
def main():
n, k = map(int, input().split())
graph = defaultdict(list)
# 邻接表
for _ in range(k):
s, t = map(int, input().split())
graph[s].append(t)
visited = [False] * (n + 1) # 节点编号从1开始
#visited[1] = True
dfs(graph, visited, 1)
# 查看是否有不能到达的节点
for i in range(2, n + 1):
if visited[i] == False:
print(-1)
return
print(1) # 可以到达
if __name__ == '__main__':
main()
广搜
广搜代码如下
from collections import defaultdict
from collections import deque
def bfs(graph, visited, x):
#visited[x] = True # 入队前设置为True
que = deque()
que.append(x)
while len(que) != 0:
curx = que.popleft()
for i in graph[curx]:
if not visited[i]: # 将没有访问过的节点设置为True和入队
visited[i] = True
que.append(i)
def main():
n, k = map(int, input().split())
graph = defaultdict(list)
# 邻接表
for _ in range(k):
s, t = map(int, input().split())
graph[s].append(t)
visited = [False] * (n + 1) # 节点编号从1开始
visited[1] = True
bfs(graph, visited, 1)
# 查看是否有不能到达的节点
for i in range(2, n + 1):
if visited[i] == False:
print(-1)
return
print(1) # 可以到达
if __name__ == '__main__':
main()
106.岛屿的周长:
思路
首先分析题目,矩阵中只有一个岛屿,且岛屿的周长不是临水的陆地数,而是临水的边数之和。
实际分析下来不需要用到深搜和广搜。
思路1
遍历矩阵,当遇到陆地单元格时,计算其面对水的边数。即求其上下左右的坐标,如果是水或者超出边界,就计入岛屿的周长中
direction = [[1, 0], [0, 1], [-1, 0], [0, -1]]
def curPerimeter(grid, x, y): # 求当前单元格被水围的周长
count = 0
for i in range(4):
nextx, nexty = x + direction[i][0], y + direction[i][1]
if nextx < 0 or nextx >= len(grid) or nexty < 0 or nexty >= len(grid[0]):
count += 1
continue
if grid[nextx][nexty] == 0: # 周围是水
count += 1
return count
def main():
n, m = map(int, input().split())
grid = []
for _ in range(n):
grid.append(list(map(int, input().split())))
perimeter = 0
for x in range(n):
for y in range(m):
if grid[x][y]: # 记录陆地临水的周长
perimeter += curPerimeter(grid, x, y)
print(perimeter)
if __name__ == '__main__':
main()
思路2
首先得到岛屿中的陆地数,陆地数4 - 相邻陆地的边数2
因为如果有一对陆地相邻,那么就有两个单元格就有重复的边被包括在了岛屿中,那么边数就要 - 2。
比如说下图中红色的线是一个示例
因此最终岛屿的周长 = 陆地数 * 4 - 相邻陆地数 * 2
direction = [[1, 0], [0, 1], [-1, 0], [0, -1]]
def main():
n, m = map(int, input().split())
grid = []
for _ in range(n):
grid.append(list(map(int, input().split())))
landNum, cover = 0, 0 # 记录陆地数和相邻陆地的边数
for x in range(n):
for y in range(m):
if grid[x][y]:
landNum += 1
# 统计上边相邻的陆地的边数
if x - 1 >= 0 and grid[x - 1][y]:
cover += 1
# 统计左边的相邻的陆地的边数
if y - 1 >= 0 and grid[x][y - 1]:
cover += 1
# 不统计下边和右边的,避免重复,因为后面遍历的时候会计算上边和左边的,如果当前算下和右,相邻的边数就重复了
print(landNum * 4 - cover * 2)
if __name__ == '__main__':
main()
学习收获:
- 字符串接龙:要先将beginStr、endStr和strList中的字符串联合成无向图,理解是求无向图中从某个节点到另一个节点的最短路径。但是不能将其转换为图,只是通过字符串之间相差的字符数来判断是否存在边。使用广搜。需要记录访问过的节点和路径长度
- 有向图的完全可达性:就是节点1能否到达任意节点,注意是有向图,使用任意遍历方式均可(深搜/广搜)。以及深搜注意没有用到回溯
- 岛屿的周长:两种方式,没有用到深搜 / 广搜。两种方式,要么直接计算临水的边数;要么计算岛屿内相邻的边数,然后再进行处理