Partition Array Into Two Arrays to Minimize Sum Difference

hard dp meet in the middle bitmask

Problem

You are given an integer array nums of 2 * n integers. You need to partition nums into two arrays of length n to minimize the absolute difference of the sums of the arrays. Return the minimum possible absolute difference.

Inputnums = [3,9,7,3]
Output2
n = 2. Choosing {3,9} vs {7,3} gives sums 12 and 10, difference 2. We pick n items in total and want the chosen sum as close to total/2 as possible.

from bisect import bisect_left, insort

def minimum_difference(nums):
    n = len(nums) // 2
    total = sum(nums)
    A, B = nums[:n], nums[n:]
    # group subset sums of each half by how many elements chosen
    def sums_by_count(half):
        m = len(half)
        groups = [[] for _ in range(m + 1)]
        for mask in range(1 << m):
            cnt = bin(mask).count("1")
            s = sum(half[i] for i in range(m) if mask & (1 << i))
            groups[cnt].append(s)
        return groups
    GA, GB = sums_by_count(A), sums_by_count(B)
    best = float("inf")
    for k in range(n + 1):
        right = sorted(GB[n - k])
        for sa in GA[k]:
            # pick k from A, n-k from B; want left close to total/2
            target = total / 2 - sa
            j = bisect_left(right, target)
            for cand in (right[j - 1] if j else None, right[j] if j < len(right) else None):
                if cand is None:
                    continue
                left = sa + cand
                best = min(best, abs(total - 2 * left))
    return best
function minimumDifference(nums) {
  const n = nums.length / 2, total = nums.reduce((a, b) => a + b, 0);
  const A = nums.slice(0, n), B = nums.slice(n);
  function sumsByCount(half) {
    const m = half.length, groups = Array.from({ length: m + 1 }, () => []);
    for (let mask = 0; mask < (1 << m); mask++) {
      let cnt = 0, s = 0;
      for (let i = 0; i < m; i++) if (mask & (1 << i)) { cnt++; s += half[i]; }
      groups[cnt].push(s);
    }
    return groups;
  }
  const GA = sumsByCount(A), GB = sumsByCount(B);
  let best = Infinity;
  for (let k = 0; k <= n; k++) {
    const right = GB[n - k].slice().sort((a, b) => a - b);
    for (const sa of GA[k]) {
      const target = total / 2 - sa;
      let lo = 0, hi = right.length;
      while (lo < hi) { const mid = (lo + hi) >> 1; if (right[mid] < target) lo = mid + 1; else hi = mid; }
      for (const j of [lo - 1, lo]) {
        if (j < 0 || j >= right.length) continue;
        const left = sa + right[j];
        best = Math.min(best, Math.abs(total - 2 * left));
      }
    }
  }
  return best;
}
class Solution {
    public int minimumDifference(int[] nums) {
        int n = nums.length / 2, total = 0;
        for (int x : nums) total += x;
        List<List<Integer>> GA = sums(nums, 0, n), GB = sums(nums, n, n);
        int best = Integer.MAX_VALUE;
        for (int k = 0; k <= n; k++) {
            List<Integer> right = GB.get(n - k);
            Collections.sort(right);
            for (int sa : GA.get(k)) {
                int target = total / 2 - sa;
                int lo = lowerBound(right, target);
                for (int j : new int[]{lo - 1, lo}) {
                    if (j < 0 || j >= right.size()) continue;
                    int left = sa + right.get(j);
                    best = Math.min(best, Math.abs(total - 2 * left));
                }
            }
        }
        return best;
    }
    private List<List<Integer>> sums(int[] nums, int start, int m) {
        List<List<Integer>> g = new ArrayList<>();
        for (int c = 0; c <= m; c++) g.add(new ArrayList<>());
        for (int mask = 0; mask < (1 << m); mask++) {
            int cnt = 0, s = 0;
            for (int i = 0; i < m; i++) if ((mask & (1 << i)) != 0) { cnt++; s += nums[start + i]; }
            g.get(cnt).add(s);
        }
        return g;
    }
    private int lowerBound(List<Integer> a, int t) {
        int lo = 0, hi = a.size();
        while (lo < hi) { int mid = (lo + hi) >>> 1; if (a.get(mid) < t) lo = mid + 1; else hi = mid; }
        return lo;
    }
}
int minimumDifference(vector<int>& nums) {
    int n = nums.size() / 2, total = 0;
    for (int x : nums) total += x;
    auto sums = [&](int start, int m) {
        vector<vector<int>> g(m + 1);
        for (int mask = 0; mask < (1 << m); mask++) {
            int cnt = __builtin_popcount(mask), s = 0;
            for (int i = 0; i < m; i++) if (mask & (1 << i)) s += nums[start + i];
            g[cnt].push_back(s);
        }
        return g;
    };
    auto GA = sums(0, n), GB = sums(n, n);
    int best = INT_MAX;
    for (int k = 0; k <= n; k++) {
        auto& right = GB[n - k];
        sort(right.begin(), right.end());
        for (int sa : GA[k]) {
            int target = total / 2 - sa;
            int j = lower_bound(right.begin(), right.end(), target) - right.begin();
            for (int t : {j - 1, j}) {
                if (t < 0 || t >= (int)right.size()) continue;
                int left = sa + right[t];
                best = min(best, abs(total - 2 * left));
            }
        }
    }
    return best;
}
Time: O(2n·n) for 2n elements Space: O(2n)