📜  门| GATE CS 2021 |设置 2 |问题 27(1)

📅  最后修改于: 2023-12-03 15:42:12.958000             🧑  作者: Mango

门 | GATE CS 2021 | 设置 2 | 问题 27

题目描述

给定一个仅包含 0 和 1 的二进制字符串 A,和一个整数 B,你需要将 A 转换为长度为 B 的二进制字符串,通过以下操作:

  1. 将 A 中的某个数位取反,这个操作的代价是 1;
  2. 将 A 中的相邻两个位取反,这个操作的代价是 1。

求将 A 转换为长度为 B 的二进制字符串的最小代价。

具体输入格式请查看 GATE CS 2021 设置 2 问题 27

解题思路

首先可以想到可以用动态规划来解决这个问题。我们可以创建一个 $dp_{i,j}$ 数组来表示将 A 的前 $i$ 位转换为长度为 $j$ 的二进制字符串的最小代价。那么可以列出如下转移方程:

$$ dp_{i,j}=\begin{cases} dp_{i-1,j-1}&\text{if }A_{i}=a_{j}\ dp_{i-1,j-1}+\min(1, w(A_{i},j))&\text{if }A_{i}\ne a_{j}\ dp_{i-1,j}+\min{w(a_{j},a_{j-1}),w(a_{j+1},a_j)}&\text{if }j>1\ dp_{i-1,1}+\min{w(A_i, a_1), w(a_1, a_2)} &\text{if } j=1 \end{cases} $$

其中 $w(x,y)$ 为将 $x$ 替换成 $y$ 的代价。可以发现,当 $A_i$ 和 $a_j$ 相同时,将 $A_i$ 换成 $a_j$ 没有代价。否则,可以选择将 $A_i$ 直接变成 $a_j$ 或者将相邻两个数取反。为了方便,可以令 $A_0$ 为 $0$。注意到 $j$ 可以是 1~B。

代码实现
def min_cost(A: str, B: int) -> int:
    a = "0" * (B + 2)
    dp = [[float('inf')] * (B + 1) for _ in range(len(A))]
    a = a[:B+1]
    dp[0][1] = w(A[0], a[1])
    for j in range(2, B + 1):
        dp[0][j] = dp[0][j-1] + w(a[j-1], a[j])
    for i in range(1, len(A)):
        dp[i][1] = dp[i-1][1] + w(A[i], a[1])
        for j in range(2, B + 1):
            if A[i] == a[j]:
                dp[i][j] = dp[i-1][j-1]
            else:
                dp[i][j] = dp[i-1][j-1] + min(1, w(A[i], a[j]))
            dp[i][j] = min(dp[i][j], dp[i-1][j] + w(a[j-1], a[j]) )
            if j == B:
                dp[i][j] = min(dp[i][j], dp[i-1][j-1] + w(A[i], a[1]))
            else:
                dp[i][j] = min(dp[i][j], dp[i-1][j-1] + w(A[i], a[1]) + w(a[2], a[1]))
                dp[i][j] = min(dp[i][j], dp[i-1][j-1] + w(A[i], a[j+1]) + w(a[j], a[j+1]))
    return dp[len(A)-1][B]

def w(x: str, y: str) -> int:
    if x == y:
        return 0
    else:
        return 1
时间复杂度

这个算法的时间复杂度为 $O(nB)$,其中 $n$ 是字符串 A 的长度。