豆包MarsCode 算法竞技赛 Vol.1 | 第三、四题(602金银珠宝的数值)

195 阅读2分钟

第三题思路

计算存储二维前缀和,二维前缀和与概率论中概率分布函数的计算类似。有了前缀和之后对应矩形的面积计算复杂度为常数级与矩形边长无关。

areasub[i][j]=memo[i][j]memo[il][j]memo[i][jl]+memo[il][jl]\begin{align} \text{area}_{\text{sub}}[i][j] & = \text{memo}[i][j] \\ & -\text{memo}[i-l][j] \\ & -\text{memo}[i][j-l] \\ & +\text{memo}[i-l][j-l] \end{align}

注意边界处理即可。

第四题(602金银珠宝的数值

思路

典型的区间DP

代码

def solution(stones, c):
    n = len(stones)
    m = len(c)

    # dp[i][j] 表示从stones[i]到stones[j]的区间获得最大财富
    dp = [[float('-inf')] * n for _ in range(n)]
    # 初始化边界
    for i in range(n-1-(n-m+1)+1+1):
        dp[i][i+n-m] = max(stones[i]*c[-1], stones[i+n-m]*c[-1])

    # 动态规划填表
    for ki, length in enumerate(range(n-m+2, n + 1), start=2):  # length表示当前考虑的全闭区间长度
        for i in range(n - length+1):
            j = i + length - 1
            k = m - ki
            if i > j:
                break
            # 选择顶部的珠宝
            dp[i][j] = max(dp[i][j], stones[i] * c[k] + (dp[i + 1][j] if i + 1 <= j else 0))
            # 选择底部的珠宝
            dp[i][j] = max(dp[i][j], stones[j] * c[k] + (dp[i][j - 1] if i <= j - 1 else 0))

    # 返回从0到n-1区间的最大财富
    return dp[0][n - 1]

注意

  1. 初始化边界
  2. 状态转移边界
  3. 区间此处我选择的是全闭,注意i,ji, j取值边界与意义

官方题解

官方给出的是依据符文序列的一维DP,给出了一种状态压缩方案。

代码

def solution(stones: list, c: list) -> int:
    n, m = len(stones), len(c)
    dp = [0] * (m+1)
    for i in range(m-1, -1, -1):
        prev = dp[i+1]
        for left in range(i, -1, -1):
            next_val = dp[left]
            dp[left] = max(c[i]*stones[left]+prev, c[i]*stones[n-1-i+left]+dp[left])
            prev = next_val
     return dp[0]

背包的状态压缩有时依赖时间次序,表中有些位置可以用来作为缓冲。 dp[i+1]保存的是轮次结束的值,dp[left]在max函数中的第二项保存的是上一轮的结果,结果是区间dp长度为i-1的结果。时序上不重叠导致dp的空间压缩。