467 牛妹的K次奇怪操作| 豆包MarsCode AI刷题

168 阅读3分钟

问题描述

牛妹有一个序列 a1,a2,…,an,她可以对序列执行 kk 次奇怪的操作。每次操作的步骤如下:

  1. 牛妹选择一个位置 pos(1≤pos≤n)。
  2. 将 apos在二进制表示下最低位的 11 变成 00。

牛妹想知道在执行 k 次操作之后,序列的总和最小能是多少。


测试样例

样例1:

输入:n = 2 ,k = 1 ,a = [8, 7]
输出:7

样例2:

输入:n = 2 ,k = 2 ,a = [9, 6]
输出:6

样例3:

输入:n = 3 ,k = 2 ,a = [3, 5, 7]
输出:10

解题思路

我们希望在最多 k 次操作后,使序列和最小化。由于每次操作只能消除一个最低位的 1,我们可以通过动态规划(DP)记录操作过程中的最优结果。

1. 二进制最低位 1 的处理

通过位运算,我们可以高效找到并移除二进制数的最低位 1
设 x 是当前数字:

  • 使用 -x & x 找到最低位的 1
  • 使用 x -= (-x & x) 移除该位。

例如:

  • x=7(111),最低位的 11
  • x=6(110),最低位的 12

2. 动态规划转移方程

状态定义

  • dp[i][j]:前 i 个数,使用最多 j 次操作后,可以得到的最大消除值。

转移方程

  • 不操作当前数: dp[i][j]=dp[i−1][j]
  • 使用 t 次操作在当前数上: dp[i][j]=max⁡(dp[i][j],dp[i−1][j−t]+sum(l[i][:t]))其中 l[i]是当前数的所有最低位 1 组成的列表,sum(l[i][:t]) 表示操作 t 次后的累计消除值。

最终答案

  • sum(a)−dp[n][k]即原始总和减去最大可消除值。

代码实现

def solution(n: int, k: int, a: list) -> int:
    dp = [[0 for i in range(k+1)] for j in range(n+1)]
    for i in range(k+1):
        dp[0][i]=0
    for j in range(n+1):
        dp[j][0]=0
    sum_a = sum(a)
    l = []
    for i in range(n):
        temp = []
        while a[i]>0:
            temp.append(-a[i]&a[i])
            a[i]-=temp[-1]
        l.append(temp)
    for i in range(1,n+1):
        for j in range(1,k+1):
            temp = []
            for num in range(min(len(l[i-1]),j)+1):
                temp.append(dp[i-1][j-num]+sum(l[i-1][:num]))
            dp[i][j] = max(temp)
    return sum_a-dp[n][k]


if __name__ == '__main__':
    print(solution(14, 16, [8,10,14,5,16,7,5,14,17,11,10,14,16,9]),47)
    print(solution(2, 2, [9, 6]) == 6)
    print(solution(3, 2, [3, 5, 7]) == 10)

复杂度分析

  1. 时间复杂度

    • 预处理 l 阶段:每个数最多有 log⁡(max(a))个最低位 1,总时间复杂度为 O(n⋅log⁡(max(a)))。
    • 动态规划阶段:遍历 DP 表,每个单元格最多枚举 k次操作,复杂度为 O(n⋅k2)。
    • 综合复杂度为 O(n⋅(log⁡(max(a))+k2))。
  2. 空间复杂度

    • DP 表占用 O(n⋅k) 的空间。

总结

通过位运算和动态规划的结合,我们可以高效解决这类与二进制操作相关的优化问题。本题中的关键是构建合适的状态转移方程,并灵活使用位运算简化预处理步骤。