刷题小记4 | 豆包MarsCode AI刷题

205 阅读5分钟

小M的星球时代冒险

问题描述

小M最近沉迷于一款叫《孢子》的游戏,游戏中,小M需要在一个星球上完成跑腿任务,提升自己。星球的地图可以抽象为一个nm列的矩阵,小M每次可以往上下左右任意方向移动一格。若小M往地图边缘移动,他会出现在另一边。例如,小M在第m列时往右移动会出现在第1列;在第1行时往上移动会出现在第n行。
这个星球的地图由一个生成器生成,分为两种地形。地图的生成规则如下:

  • 生成两个数列a[1...n]b[1...m],其中的值为01
  • 对于坐标(i,j)的点,若a[i] == b[j],则该点为地形A,否则为地形B。

小M需要从一个起点(x1, y1)移动到终点(x2, y2)执行任务。小M想知道他最少需要跨越多少次地形来完成任务。

  • n 表示地图的行数。
  • m 表示地图的列数。
  • a 是长度为n的列表,表示每一行的地形类型(值为01)。
  • b 是长度为m的列表,表示每一列的地形类型(值为01)。
  • 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

思路

  1. 动态规划:我们可以使用动态规划来解决这个问题。定义一个二维数组 dp,其中 dp[i][j] 表示将 dna1 的前 i 个字符转换成 dna2 的前 j 个字符所需的最少编辑步骤。

  2. 初始化

    • dp[0][j] 表示将空字符串转换成 dna2 的前 j 个字符,需要 j 次增加操作。
    • dp[i][0] 表示将 dna1 的前 i 个字符转换成空字符串,需要 i 次删除操作。
  3. 状态转移

    • 如果 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]