给你一个长度为 2 * n 的整数数组。你需要将 nums 分成 两个 长度为 n 的数组,分别求出两个数组的和,并 最小化 两个数组和之 差的绝对值 。nums 中每个元素都需要放入两个数组之一。
请你返回 最小 的数组和之差。
示例 1:
输入:nums = [3,9,7,3]
输出:2
解释:最优分组方案是分成 [3,9] 和 [7,3] 。
数组和之差的绝对值为 abs((3 + 9) - (7 + 3)) = 2 。
示例 2:
输入:nums = [-36,36]
输出:72
解释:最优分组方案是分成 [-36] 和 [36] 。
数组和之差的绝对值为 abs((-36) - (36)) = 72 。
示例 3:
输入:nums = [2,-1,0,4,-2,-9]
输出:0
解释:最优分组方案是分成 [2,4,-9] 和 [-1,0,-2] 。
数组和之差的绝对值为 abs((2 + 4 + -9) - (-1 + 0 + -2)) = 0 。
提示:
1 <= n <= 15
nums.length == 2 * n
-1e7 <= nums[i] <= 1e7
源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/partition-array-into-two-arrays-to-minimize-sum-difference
解法:
每个元素都有两种情况(加号,减号)一开始想的是传化为01背包问题,但元素的和很大,超出背包容量,并且背包做法无法确保等分数组。参考其它人题解,当数组长度不大(n<=20),可以枚举出所有情况。本题数组长度最大为30,无法直接枚举()。故需用折半查找,那么一半的数组长度为15,则可以直接枚举。利用二进制来枚举,二进制数数位为1的,赋予加号,和为s1,减号和为s2.另一半加号和s3,减号和为s4.同时记录1的个数。具体查看代码
class Solution {
void travel(vector<vector<int>> &v, int st, int n, vector<int> &nums)
{
for (int i = 0; i < (1 << n); i++) //每个位置非正即负,可以用n位二进制来枚举
{
int sum = 0;
int cnt = 0;
for (int j = 0; j < n; j++)
{
if ((i >> j) & 1) //第j位为1,赋予正号
{
sum += nums[st+j]; //st=0,表示枚举前半部分
cnt++; //记录正号个数
}
else
{
sum -= nums[st+j]; //第j位为0,赋予负号
}
}
v[cnt].push_back(sum); //记录正号个数为cnt个,数组和sum1=s1-s2。
}
}
public:
int minimumDifference(vector<int>& nums) {
int n = nums.size() / 2;
vector<vector<int> > v1(n + 1); //v1[i]表示正号个数为i时,其余为符号,数组和的情况
travel(v1, 0, n, nums); //枚举前一半。
int ans = INT_MAX;
for (int i = 0; i <= n; i++)sort(v1[i].begin(), v1[i].end()); //对前半部分排序,为了后半部分与前半部分匹配查找时更快速。
for (int i = 0; i < (1 << n); i++) //枚举后一半。
{
int sum = 0; //后一半数组的和sum2=s3-s4
int cnt = 0; //正号个数
for (int j = 0; j < n; j++)
{
if ((i >> j) & 1)
{
sum += nums[n + j];
cnt++;
}
else
{
sum -= nums[n + j];
}
}
int to = n - cnt; //与前一半数组匹配,当后一半数组正号个数为cnt,则前一半为n-cnt;
//求 s1-s2+s3-s4=sum1+sum2 的绝对值最小,那么就去查找与-sum2最相近的,就是sum1+sum2最接近0.
//二分查找
int id = lower_bound(v1[to].begin(), v1[to].end(), -sum) - v1[to].begin();
if (id - 1 >= 0)
ans = min(ans, abs(sum + v1[to][id - 1]));
if (id < v1[to].size())
ans = min(ans, abs(sum + v1[to][id]));
}
return ans;
}
};