小M的星球时代冒险
问题描述
小M最近沉迷于一款叫《孢子》的游戏,游戏中,小M需要在一个星球上完成跑腿任务,提升自己。星球的地图可以抽象为一个n行m列的矩阵,小M每次可以往上下左右任意方向移动一格。若小M往地图边缘移动,他会出现在另一边。例如,小M在第m列时往右移动会出现在第1列;在第1行时往上移动会出现在第n行。
这个星球的地图由一个生成器生成,分为两种地形。地图的生成规则如下:
- 生成两个数列
a[1...n]和b[1...m],其中的值为0或1。 - 对于坐标
(i,j)的点,若a[i] == b[j],则该点为地形A,否则为地形B。
小M需要从一个起点(x1, y1)移动到终点(x2, y2)执行任务。小M想知道他最少需要跨越多少次地形来完成任务。
n表示地图的行数。m表示地图的列数。a是长度为n的列表,表示每一行的地形类型(值为0或1)。b是长度为m的列表,表示每一列的地形类型(值为0或1)。q表示任务的数量。array是一个二维列表,每个元素为[x1, y1, x2, y2],表示任务的起点和终点坐标。
测试样例
样例1:
输入:
n = 3 ,m = 4 ,a = [0, 1, 1] ,b = [0, 1, 1, 0] ,q = 2 ,array = [[2, 1, 3, 3], [2, 4, 2, 1]]
输出:[1, 0]
样例2:
输入:
n = 3 ,m = 4 ,a = [0, 1, 0] ,b = [0, 0, 1, 1] ,q = 2 ,array = [[1, 2, 2, 2], [2, 1, 2, 3]]
输出:[1, 1]
样例3:
输入:
n = 5 ,m = 5 ,a = [1, 0, 1, 0, 1] ,b = [0, 1, 0, 1, 0] ,q = 3 ,array = [[1, 1, 3, 3], [2, 5, 4, 2], [3, 4, 5, 1]]
输出:[4, 3, 3]
思路
利用广度优先搜索去找到最小值
from collections import deque
def solution1(n, m, a, b, q, array):
# Edit your code here
def get_terrain(x, y):
"""返回点(x, y)的地形类型"""
return a[x] == b[y]
def bfs(x1, y1, x2, y2):
"""计算从(x1, y1)到(x2, y2)的最少地形切换次数"""
# 起点和终点直接相等的情况
if x1 == x2 and y1 == y2:
return 0
# 队列: 存储 (x, y, switch_count)
queue = deque([(x1, y1, 0)])
visited = set([(x1, y1)])
start_terrain = get_terrain(x1, y1)
found = False
while queue and not found:
x, y, switch_count = queue.popleft()
# 检查四个方向的邻居
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
nx, ny = (x + dx) % n, (y + dy) % m
nx = nx + n if nx < 0 else nx # 确保环绕正确
ny = ny + m if ny < 0 else ny
if nx == x2 and ny == y2:
if maps[nx][ny] == maps[x][y]:
return switch_count
else:
return switch_count + 1
found = True
break
if (nx, ny) not in visited:
visited.add((nx, ny))
# 继续扩展
if maps[nx][ny] == maps[x][y]:
queue.append((nx, ny, switch_count))
else:
queue.append((nx, ny, switch_count+1))
# 理论上不会到达这里
return -1
maps = [[0 if a[i] == b[j] else 1 for j in range(m)] for i in range(n)]
results = []
for x1, y1, x2, y2 in array:
results.append(bfs(x1 - 1, y1 - 1, x2 - 1, y2 - 1))
return results
但是上述代码无法通过用例四,预计输出为37,但是地图大小仅为9*6,不太理解为什么结果为37。代码输出的结果为0,应该是正确的。
DNA序列编辑距离
问题描述
小R正在研究DNA序列,他需要一个函数来计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:增加一个碱基、删除一个碱基或替换一个碱基。
测试样例
样例1:
输入:
dna1 = "AGT",dna2 = "AGCT"
输出:1
样例2:
输入:
dna1 = "AACCGGTT",dna2 = "AACCTTGG"
输出:4
样例3:
输入:
dna1 = "ACGT",dna2 = "TGC"
输出:3
样例4:
输入:
dna1 = "A",dna2 = "T"
输出:1
样例5:
输入:
dna1 = "GGGG",dna2 = "TTTT"
输出:4
思路
-
动态规划:我们可以使用动态规划来解决这个问题。定义一个二维数组
dp,其中dp[i][j]表示将dna1的前i个字符转换成dna2的前j个字符所需的最少编辑步骤。 -
初始化:
dp[0][j]表示将空字符串转换成dna2的前j个字符,需要j次增加操作。dp[i][0]表示将dna1的前i个字符转换成空字符串,需要i次删除操作。
-
状态转移:
-
如果
dna1[i-1] == dna2[j-1],则dp[i][j] = dp[i-1][j-1],因为不需要任何编辑操作。 -
否则,
dp[i][j]可以通过以下三种操作之一得到:- 增加:
dp[i][j-1] + 1 - 删除:
dp[i-1][j] + 1 - 替换:
dp[i-1][j-1] + 1
- 增加:
-
取这三种操作的最小值作为
dp[i][j]。
-
代码
def solution(dna1, dna2):
# Please write your code here
dp = [[0] * (len(dna2)+1) for _ in range(len(dna1)+1)]
for i in range(1,len(dna2)+1):
dp[0][i] = i
for i in range(1,len(dna1)+1):
dp[i][0] = i
for i in range(1,len(dna1)+1):
for j in range(1,len(dna2)+1):
if dna1[i-1] == dna2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
add = dp[i][j-1] + 1
delete = dp[i-1][j] + 1
rep = dp[i-1][j-1] + 1
dp[i][j] = min(min(add,delete),rep)
return dp[-1][-1]