1681. Minimum Incompatibility

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 02n1。最后的目标就是 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[ij]+cost[j],cost表示状态j的Incompatibility,预处理计算,复杂度 O ( n ∗ 2 n ) O(n*2^n) O(n2n)。要注意这里的 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;
        
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值