1681. 最小不兼容性
题意
给你一个整数数组 nums 和一个整数 k 。你需要将这个数组划分到 k 个相同大小的子集中,使得同一个子集里面没有两个相同的元素。
一个子集的 不兼容性 是该子集里面最大值和最小值的差。
请你返回将数组分成 k 个子集后,各子集 不兼容性 的 和 的 最小值 ,如果无法分成分成 k 个子集,返回 -1 。
子集的定义是数组中一些数字的集合,对数字顺序没有要求。
样例输入
nums = [1,2,1,4], k = 2
样例输出
4
提示
1 <= k <= nums.length <= 16
nums.length 能被 k 整除。
1 <= nums[i] <= nums.length
c++代码
class Solution {
public:
int minimumIncompatibility(vector<int>& nums, int k) {
int n = nums.size();
int group = n / k;
vector<int> dp((1 << n), INT_MAX);
dp[0] = 0;
unordered_map<int, int> values;
for(int mask = 1; mask < (1 << n); mask++ ){
if(__builtin_popcount(mask) != group) continue;
int mx = INT_MIN, mn = INT_MAX;
unordered_set<int> cur;
for(int i = 0; i < n; i++){
if(mask & (1 << i)){
if(cur.count(nums[i]) > 0) break;
cur.insert(nums[i]);
mx = max(mx, nums[i]);
mn = min(mn, nums[i]);
}
if(cur.size() == group) values[mask] = mx-mn;
}
}
for(int mask = 0; mask < (1 << n); mask++ ){
if(dp[mask] == INT_MAX) continue;
unordered_map<int, int> seen;
int sub = 0;
for(int i = 0; i < n; i++ ){
if((mask & (1 << i)) == 0){
seen[nums[i]] = i;
}
}
for(auto& i : seen){
sub = sub | (1 << i.second);
}
int next = sub;
while(next > 0){
if(values.count(next) != 0){
dp[mask | next] = min(dp[mask | next], dp[mask] + values[next]);
}
next = (next - 1) & sub;
}
}
return dp[(1 << n) - 1] != INT_MAX ? dp[(1 << n) - 1] : -1;
}
};
python代码
from typing import *
class Solution:
def minimumIncompatibility(self, nums: List[int], k: int) -> int:
n = len(nums);
group = n // k;
dp = [inf] * (1 << n)
dp[0] = 0
values = {}
for mask in range(1 << n):
if mask.bit_count() != group:
continue
mx = -inf
mn = inf
cur = set()
for i in range(n):
if (mask & (1 << i)) > 0:
if nums[i] in cur:
break
cur.add(nums[i])
mx = max(mx, nums[i])
mn = min(mn, nums[i])
if len(cur) == group: values[mask] = mx - mn
for mask in range(1 << n):
if dp[mask] == inf:
continue
seen = {}
for i in range(n):
if (mask & (1 << i)) == 0:
seen[nums[i]] = i
if len(seen) < group: continue
sub = 0
for i in seen:
sub = sub | (1 << seen[i])
nxt = sub
while nxt > 0:
if nxt in values:
dp[mask | nxt] = min(dp[mask | nxt], dp[mask] + values[nxt])
nxt = (nxt - 1) & sub
return dp[-1] if dp[-1] < inf else -1
备注
好难一题,总体流程如下。首先用二进制码是否为1/0来标记数组中的元素是否存在集合中,再然后对数据做预处理,找到所有可能的集合并记录其不兼容性。然后从mask为0遍历所有子串,转移方程为dp[mask|sub] = min(dp[mask|sub], dp[mask] + values[sub])