Source: https://leetcode.com/contest/weekly-contest-218/problems/minimum-incompatibility/
You are given an integer array nums and an integer k. You are asked to distribute this array into k subsets of equal size such that there are no two equal elements in the same subset.
A subset’s incompatibility is the difference between the maximum and minimum elements in that array.
Return the minimum possible sum of incompatibilities of the k subsets after distributing the array optimally, or return -1 if it is not possible.
A subset is a group integers that appear in the array with no particular order.
Example 1:
Input: nums = [1,2,1,4], k = 2
Output: 4
Explanation: The optimal distribution of subsets is [1,2] and [1,4].
The incompatibility is (2-1) + (4-1) = 4.
Note that [1,1] and [2,4] would result in a smaller sum, but the first subset contains 2 equal elements.
Example 2:
Input: nums = [6,3,8,1,3,1,2,2], k = 4
Output: 6
Explanation: The optimal distribution of subsets is [1,2], [2,3], [6,8], and [1,3].
The incompatibility is (2-1) + (3-2) + (8-6) + (3-1) = 6.
Example 3:
Input: nums = [5,3,3,6,3,3], k = 3
Output: -1
Explanation: It is impossible to distribute nums into 3 subsets where no two elements are equal in the same subset.
Constraints:
1 <= k <= nums.length <= 16
nums.length is divisible by k
1 <= nums[i] <= nums.length
以下吸收自大佬:
一看这题的数据范围,知道复杂度可以指数次,那么肯定就是状态压缩dp了。
n个数,均分k组,每组n/k个
令
f
[
i
]
f[i]
f[i]表示已经凑好了若干组且已用的数为i的所有方案中Incompatibility的最小值
这里的 i 肯定是十进制数,但是它的二进制表示很有意思,有n位,每位为1表示原数组里对应的数被选了,为0表示没被选。i的范围是从全0,000…000, 到全1,111…111,总共 2 n 2^n 2n个数,放成数组下标则为 0 ∼ 2 n − 1 0\sim2^n-1 0∼2n−1。最后的目标就是 f ( 111...111 ) = f [ ( 1 < < n ) − 1 ] f(111...111)=f[(1<<n)-1] f(111...111)=f[(1<<n)−1]
令
j
j
j表示选择的最后一组n/k个数的集合,j就是i的子集了。枚举i就是for(int i = 1; i < 1 << n; i++)
,枚举i的所有子集是for(int j = i; j; j = j - 1 & i)
,这个枚举的复杂度是
3
n
3^n
3n。(据说是二项式定理)
dp递推是 f [ i ] = min j f [ i − j ] + c o s t [ j ] f[i]=\min\limits_j f[i-j]+cost[j] f[i]=jminf[i−j]+cost[j],cost表示状态j的Incompatibility,预处理计算,复杂度 O ( n ∗ 2 n ) O(n*2^n) O(n∗2n)。要注意这里的 j 只有在包括n/k个元素时才加入计算,即都是按组枚举的,否则值为-1。
class Solution {
public:
int minimumIncompatibility(vector<int>& nums, int k) {
int n=nums.size(), INF=1e8;
vector<int> f(1<<n,INF);
vector<int> cost(1<<n);
//这段计算cost
int d[16];
for(int i=1;i<1<<n;i++){
cost[i]=-1;
if(__builtin_popcount(i)==n/k){
// 下面这段是把选出的数从原数组nums写进d里
int cnt=0;
for(int j=0;j<n;j++){
if(i>>j&1){
d[cnt++]=nums[j];
}
}
sort(d,d+cnt);
int flag=1,minv=d[0],maxv=d[0];
for(int j=1;j<cnt;j++){
if(d[j]==d[j-1]){
flag=0;
break;
}
}
if(flag){
cost[i]=d[cnt-1]-d[0];
}
}
}
//这段算f
f[0]=0;
for(int i=1;i<1<<n;i++){
for(int j=i;j;j=j-1&i){
if(cost[j]!=-1)
f[i]=min(f[i-j]+cost[j],f[i]);
}
}
int res=f[(1<<n)-1];
if(res==INF){
res=-1;
}
return res;
}
};