LeetCode 3117 - Minimum Sum of Values by Dividing Array
This problem asks us to partition an array nums into exactly m contiguous subarrays such that the bitwise AND of elements in the i-th subarray equals andValues[i].
Difficulty: 🔴 Hard
Topics: Array, Binary Search, Dynamic Programming, Bit Manipulation, Segment Tree, Queue
Solution
Problem Understanding
This problem asks us to partition an array nums into exactly m contiguous subarrays such that the bitwise AND of elements in the i-th subarray equals andValues[i]. Once partitioned, we sum the last elements of each subarray to compute a total value, and the goal is to minimize this sum. If no valid partition exists, we return -1.
In other words, we are looking for contiguous subarrays [li, ri] where the AND of all elements equals the target andValues[i], and among all valid partitions, we want the sum of the last elements to be minimal. This is a constrained partition problem combined with bitwise operations.
The constraints tell us that the input size is moderate for n (1 <= n <= 10^4), but m is very small (m <= 10). The nums[i] values are up to 10^5, so we can work directly with integers without worrying about overflow. The small m suggests a dynamic programming or recursive approach over subarray indices is feasible.
Edge cases include arrays where no subarray satisfies a target AND, arrays of length 1, and arrays where the only possible partitions are single-element subarrays.
Approaches
The brute-force approach would try all possible ways to split nums into m contiguous subarrays and check if each subarray's AND equals the corresponding andValues[i]. For each valid partition, we calculate the sum of the last elements and take the minimum. This approach is correct but exponentially slow because the number of partitions grows combinatorially with n and m. Specifically, for n elements and m splits, the number of partitions is roughly C(n-1, m-1), which is infeasible for n up to 10^4.
The optimal approach leverages dynamic programming with bitwise AND propagation. The key observation is that the AND operation is monotonic: if AND(a, b, c) = x and we extend it with a new number d, the result can only decrease (set more bits to 0). This allows us to try contiguous subarrays in order, updating the current AND and only considering subarrays that satisfy the target AND. Using a DP array dp[i][j] representing the minimal sum using the first i elements to form the first j subarrays allows us to efficiently compute the answer.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(C(n-1, m-1) * n) | O(n) | Tries all partitions, infeasible for large n |
| Dynamic Programming | O(n * m * n) worst-case, but pruning reduces it | O(n * m) | Use DP + bitwise AND monotonicity to prune invalid partitions |
Algorithm Walkthrough
- Initialize a DP array
dp[i][j]wheredp[i][j]is the minimum sum to partitionnums[0:i]intojsubarrays satisfying theANDtargets. Fill with infinity initially, exceptdp[0][0] = 0. - Iterate over the current number of subarrays
jfrom 1 tom. - For each
i(end index of the current subarray), try extending backward to find all starting indicesksuch thatnums[k:i]satisfiesAND(nums[k:i]) == andValues[j-1]. - If a valid subarray is found, update
dp[i][j]asmin(dp[i][j], dp[k][j-1] + nums[i-1])because the last element of the subarray contributes to the sum. - After filling the DP table, the answer is
dp[n][m]if it is not infinity, otherwise return-1.
Why it works: The DP keeps track of all minimal sums for partitions of increasing subarray count. By checking all backward subarrays using bitwise AND, we ensure that we only consider valid subarrays, and the DP ensures minimal sums are propagated.
Python Solution
from typing import List
class Solution:
def minimumValueSum(self, nums: List[int], andValues: List[int]) -> int:
n, m = len(nums), len(andValues)
INF = float('inf')
dp = [[INF] * (m + 1) for _ in range(n + 1)]
dp[0][0] = 0
for j in range(1, m + 1):
target = andValues[j - 1]
for i in range(1, n + 1):
curr_and = nums[i - 1]
for k in range(i, 0, -1):
curr_and &= nums[k - 1]
if curr_and < target:
break
if curr_and == target:
dp[i][j] = min(dp[i][j], dp[k - 1][j - 1] + nums[i - 1])
return dp[n][m] if dp[n][m] != INF else -1
The Python solution initializes a 2D DP table, iterates through the number of subarrays, and uses a nested loop to check all valid subarrays ending at each index using backward iteration. The AND propagation is used to prune invalid candidates early.
Go Solution
func minimumValueSum(nums []int, andValues []int) int {
n, m := len(nums), len(andValues)
INF := 1 << 60
dp := make([][]int, n+1)
for i := range dp {
dp[i] = make([]int, m+1)
for j := range dp[i] {
dp[i][j] = INF
}
}
dp[0][0] = 0
for j := 1; j <= m; j++ {
target := andValues[j-1]
for i := 1; i <= n; i++ {
currAnd := nums[i-1]
for k := i; k >= 1; k-- {
currAnd &= nums[k-1]
if currAnd < target {
break
}
if currAnd == target {
if dp[k-1][j-1]+nums[i-1] < dp[i][j] {
dp[i][j] = dp[k-1][j-1] + nums[i-1]
}
}
}
}
}
if dp[n][m] == INF {
return -1
}
return dp[n][m]
}
The Go solution mirrors the Python logic. Slices are used for the DP table, and the INF value is represented using a large integer. The backward iteration and bitwise AND pruning is identical.
Worked Examples
Example 1: nums = [1,4,3,3,2], andValues = [0,3,3,2]
| i | j | Subarray checked | curr_and | dp[i][j] |
|---|---|---|---|---|
| 2 | 1 | [1,4] | 0 | 4 |
| 3 | 2 | [3] | 3 | 7 |
| 4 | 3 | [3] | 3 | 10 |
| 5 | 4 | [2] | 2 | 12 |
Final answer: 12
Example 2: nums = [2,3,5,7,7,7,5], andValues = [0,7,5]
Optimal partition found: [[2,3,5],[7,7,7],[5]] with sum 5+7+5=17
Example 3: nums = [1,2,3,4], andValues = [2]
No valid partition exists, return -1.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n * m * n) worst-case | For each subarray count, check all ending indices and backward subarrays; pruning reduces actual iterations |
| Space | O(n * m) | DP table stores minimal sums for each prefix and subarray count |
The DP ensures we explore only valid partitions efficiently, and the backward AND propagation helps prune impossible subarrays quickly.
Test Cases
# Provided examples
assert Solution().minimumValueSum([1,4,3,3,2], [0,3,3,2]) == 12
assert Solution().minimumValueSum([2,3,5,7,7,7,5], [0,7,5]) == 17
assert Solution().minimumValueSum([1,2,3,4], [2]) == -1
# Edge cases
assert Solution().minimumValueSum([1], [1]) == 1 # Single element array matches target
assert Solution().minimumValueSum([1], [0]) == -1 # Single element cannot match target 0
assert Solution().minimumValueSum([7,7,7], [7,7,7]) == 21 # All elements match, sum last elements
assert Solution().minimumValueSum([1,2,3], [0