LC 100259. 划分数组得到最小的值之和
https://leetcode.cn/problems/minimum-sum-of-values-by-dividing-array/description/
这题目题解比较简单,在DP里面的话把 `andValue` 一起加入到了状态里面去了。之前没有仔细想过这个状态数量问题,这个我觉得题解解释比较清楚:因为And操作每次都会去消耗bit或者是保持bit, 所以其实 `andValue` 这个空间接近常量 \(log(max(nums))\)
class Solution: def minimumValueSum(self, nums: List[int], andValues: List[int]) -> int: n, m = len(nums), len(andValues) INF = 1 << 30 import functools @functools.cache def dfs(i, j, andv): if n - i < m - j: return INF if j == m: return 0 if i == n else INF andv &= nums[i] if andv < andValues[j]: return INF r = dfs(i + 1, j, andv) if andv == andValues[j]: r2 = dfs(i + 1, j + 1, -1) + nums[i] r = min(r, r2) return r ans = dfs(0, 0, -1) return ans if ans != INF else -1
我最开始的思路比较复杂,运行时间很长,但是可以说下。我没有把这个 `andValue` 纳入到dp里面去,而只是记录 `dp[i][j]` 表示 `处理到i元素的时候,划分到了j段`. 动态规划方程可以是这样的。
for i in range(n): for j in range(m): if dp[i][j] == INF or (i + 1) == n: continue if (andValues[j] & nums[i + 1]) == andValues[j]: # dp[i+1][j] <- dp[i][j] - nums[i] + nums[i+1] dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] - nums[i] + nums[i + 1]) if (j + 1) < m: # or we can create a neww group. p = find_first(i + 1, j + 1) if p != -1: dp[p][j + 1] = min(dp[p][j + 1], dp[i][j] + nums[p])
但是这其中有个函数 `find_first` 比较耗时,它做的事情就是寻找min(p), 确保 `Ands(nums[i:p+1]) = andValues[j]`. 里面使用了二分算法比较耗时。
class Solution: def minimumValueSum(self, nums: List[int], andValues: List[int]) -> int: n, m = len(nums), len(andValues) BITS = 20 acc = [[0] * BITS for _ in range(n + 1)] for i in range(n): x = nums[i] for b in range(BITS): if x & (1 << b): acc[i + 1][b] += 1 for b in range(BITS): acc[i + 1][b] += acc[i][b] def query(s, e): v = 0 for b in range(BITS): c = acc[e + 1][b] - acc[s][b] if c == (e - s + 1): v = v | (1 << b) return v import functools @functools.cache def find_first(start, index): value = andValues[index] s, e = start, n - 1 while s <= e: m = (s + e) // 2 v = query(start, m) if (v | value) == value: e = m - 1 else: s = m + 1 if s == n: return -1 v = query(start, s) if v != value: return -1 return s INF = 1 << 30 dp = [[INF] * m for _ in range(n)] p = find_first(0, 0) if p == -1: return -1 dp[p][0] = nums[p] for i in range(n): for j in range(m): if dp[i][j] == INF or (i + 1) == n: continue if (andValues[j] & nums[i + 1]) == andValues[j]: # dp[i+1][j] <- dp[i][j] - nums[i] + nums[i+1] dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] - nums[i] + nums[i + 1]) if (j + 1) < m: # or we can create a neww group. p = find_first(i + 1, j + 1) if p != -1: dp[p][j + 1] = min(dp[p][j + 1], dp[i][j] + nums[p]) ans = dp[n - 1][m - 1] return ans if ans != INF else -1