262周赛补题T4
2035. 将数组分成两个数组并最小化数组和的差
给你一个长度为 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
-107 <= nums[i] <= 107
class Solution {
public:
int minimumDifference(vector<int>& nums) {
int nn = nums.size(), n = nn / 2;
unordered_map<int, set<int>> mps;
for(int i = 0; i < 1 << n; ++ i){
int s = 0, now = 0;
for(int j = 0; j < n; ++ j){
if(i & (1 << j)){
s ++;
now += nums[j];
}else{
s --;
now -= nums[j];
}
}
mps[s].insert(now);
}
int ans = INT_MAX;
for(int i = 0; i < 1 << n; ++ i){
int s = 0, now = 0;
for(int j = 0; j < n; ++ j){
if(i & (1 << j)){
s --;
now -= nums[j + n];
}else{
s ++;
now += nums[j + n];
}
}
if(mps.find(s) != mps.end()){
auto it = mps[s].lower_bound(now);
if(it != mps[s].end()){
ans = min(ans, abs(*it - now));
}
if(it != mps[s].begin()){
it --;
ans = min(ans, abs(*it - now));
}
}
}
return ans;
}
};
算法思路
这题第一眼看到n<=15以为直接暴力dfs就可以,但事实上数据规模是2n,这样dfs肯定超时了。既然2n超时,n不超时,那我们可以把2n分成两半,然后用哈希存储第一次得到的值,第二次直接二分查找就能极大缩小时间复杂度。我们先对前n个数遍历,存储在mps中,mps是一个存储集合的哈希表,mps[i]表示前n个数中k+i个分到1组,k个分到2组的情况得到的两个数组的差,这里用状态压缩存储各种情况,举个例子
n = 3, 2n =6
110则表示nums[2]、nums[1]分到1组,nums[0]分到2组
两个数组的个数差为1
则mps[1]插入nums[2]+nums[1]-nums[0]
我们对所有情况(1<<n个)进行遍历后,对mps进行了初始化。然后对后n个数的所有情况进行遍历,也是1<<n个情况,遍历后n个数的结果则不用存入mps,只需要在mps中找到最合适的数就可以,举个例子
n = 3, 2n = 6
100则表示nums[2 + 3]分到1组,nums[1 + 3]、nums[0 + 3]分到2组
两个数组的个数差为-1,数组和差now = nums[5]-nums[4]-nums[3]
则要到mps[1]中找最合适的数
这个最合适的数就是最接近-now的数,而这里用lower_bound就能很快找到