5619. 最小不兼容性

 5619. 最小不兼容性

给你一个整数数组 nums​​​ 和一个整数 k 。你需要将这个数组划分到 k 个相同大小的子集中,使得同一个子集里面没有两个相同的元素。

一个子集的 不兼容性 是该子集里面最大值和最小值的差。

请你返回将数组分成 k 个子集后,各子集 不兼容性 的 和 的 最小值 ,如果无法分成分成 k 个子集,返回 -1 。

子集的定义是数组中一些数字的集合,对数字顺序没有要求。

 

示例 1:

输入:nums = [1,2,1,4], k = 2
输出:4
解释:最优的分配是 [1,2] 和 [1,4] 。
不兼容性和为 (2-1) + (4-1) = 4 。
注意到 [1,1] 和 [2,4] 可以得到更小的和,但是第一个集合有 2 个相同的元素,所以不可行。


示例 2:

输入:nums = [6,3,8,1,3,1,2,2], k = 4
输出:6
解释:最优的子集分配为 [1,2],[2,3],[6,8] 和 [1,3] 。
不兼容性和为 (2-1) + (3-2) + (8-6) + (3-1) = 6 。


示例 3:

输入:nums = [5,3,3,6,3,3], k = 3
输出:-1
解释:没办法将这些数字分配到 3 个子集且满足每个子集里没有相同数字。
 

提示:

1 <= k <= nums.length <= 16
nums.length 能被 k 整除。
1 <= nums[i] <= nums.length


基本思路:使用dfs,但是要注意剪枝

    int ans=INT_MAX;
    int k;
    int per;
    void dfs(vector<int> &cur,vector<int> &cnt,int tmp,int step,vector<int> &nums){
        if(tmp>ans)
            return;
        if(step==nums.size()){
            ans=min(ans,tmp);
            return ;

        }
        for(int i=0;i<k;i++){
            if(nums[step]==cur[i]) //当前集合里有相同的数字,不能放
                continue;
            if(cnt[i]<per){  //集合没有满
                int pre=cur[i];  
                cur[i]=nums[step];
                cnt[i]++;
                dfs(cur,cnt,tmp+((pre!=-1)?cur[i]-pre:0),step+1,nums);

                cnt[i]--;
                cur[i]=pre;

                if(cnt[i]==0)  //i以及i之后的集合都是空的,放那个里面都一样
                    break;
            }
        }

        return ;
    }
    int minimumIncompatibility(vector<int>& nums, int k) {
        unordered_map<int,int> dict;
        int n=nums.size();
        for(auto &it:nums){
            dict[it]++;
            if(dict[it]>k)
                return -1;
        }
        sort(nums.begin(),nums.end());
        vector<int> cur(k,-1);
        vector<int> cnt(k,0);
        this->k=k;
        this->per=n/k;
        dfs(cur,cnt,0,0,nums);
        return ans;
    }

基本思路:动态规划,先做预备工作,找到所有符合条件的小集合,然后筛选,其中,对于f[mask],mask表示的是二进制代码中为1的已经分配好集合了,而f[mask]表示的是最小兼容差

  • if f[mask]=-1,f[mask]=f[mask^sub]+value[sub] ,表示的是f[mask]可以有sub集合,以及其补集组成
  • if f[mask]=-1,f[mask]=min(f[mask],f[mask^sub]+value[sub]);
class Solution {
public:
    int minimumIncompatibility(vector<int>& nums, int k) {
        if(k==nums.size())
            return 0;
        unordered_map<int,int> dict;
        for(auto &it:nums){
            dict[it]++;
            if(dict[it]>k)
                return -1;
        }
        
        int n=nums.size();
        int M=(1<<n);
        vector<int> value(M,-1);  //预备工作,找到所有的分组
        vector<int> frequent(n+1,0);
        for(int i=1;i<M;i++){
            if(__builtin_popcount(i)==n/k){
                for(int j=0;j<n;j++){
                    if(i&(1<<j)){
                        frequent[nums[j]]++;
                    }
                }
                bool flag=true;
                for(int j=1;j<=n;j++){
                    if(frequent[j]>1){
                        flag=false;
                        break;
                    }
                }                
                
                if(flag){
                    int lb=INT_MAX,ub=INT_MIN;
                    for(int j=1;j<=n;j++){
                        if(frequent[j]>0){
                            lb=min(lb,j);
                            ub=max(ub,j);
                        }
                    }
                    value[i]=ub-lb;
                    //cout<<i<<"  "<<value[i]<<endl;
                }
                for(int j=0;j<n;j++){
                    if(i&(1<<j)){
                        --frequent[nums[j]];
                    }
                }                
            }
        }
        
        vector<int> f(M,-1);
        f[0]=0;
        for(int mask=1;mask<M;++mask){
            if(__builtin_popcount(mask)%(n/k)==0){
                for(int sub=mask;sub;sub=(sub-1)&mask){
                    if(value[sub]!=-1&&f[mask^sub]!=-1){
                        if(f[mask]!=-1){
                            f[mask]=min(f[mask],f[mask^sub]+value[sub]);//mask^sub表示的是不含sub的其他集合
                        }
                        else{
                            f[mask]=f[mask^sub]+value[sub];
                        }
                    }
                }
            }
        }
        return f[M-1];
        
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值