5619. 最小不兼容性

37 篇文章 0 订阅
11 篇文章 0 订阅

 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];
        
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
北航《精通matlab6.5》 第一章 基础准备及入门 1.1 MATLAB的安装和内容选择 1.2 Desktop操作桌面的启动 1.3 Command Window运行入门 1.4 Command Window操作要旨 1.5 Command History和实录指令diary 1.7 Workspace Browser和Array Editor 1.8 Launch Pad交互界面分类目录窗 1.9 Editor/Debugger和脚本编写初步 1.10 帮助系统 第二章 数值数组及其运算 2.1 引导 2.2 一维数组的创建和寻访 2.3 二维数组的创建 2.4 二维数组元素的标识 2.5 二维数组的子数组寻访和赋值 2.6 执行数组运算的常用函数 2.7 数组运算的矩阵运算 2.8 多项式的表达方式及其操作 2.9 标准数组生成函数和数组操作函数 2.10 数组构作技法综合 2.11 高维数组 2.12 “非数”和“空”数组 2.13 关系操作和逻辑操作 第三章 字符串、元胞和构架数组 3.1 字符串数组 3.2 元胞数组 3.3 构架数组 3.4 关于数据类型的归纳性说明 第四章 数值计算 4.1 引言 4.2 LU分解和恰定方程组的解 4.3 矩阵特征值和矩阵函数 4.4 奇异值分解 4.5 函数的数值导数和切平面 4.6 函数的零点 4.7 函数极值点 4.8 数值积分 4.9 随机数据的统计描述 4.10 多项式拟合和非线性最小二乘 4.11 插值和样条 4.12 样条函数及其应用 4.13 Fourier分析 4.14 常微分方程 4.15 稀疏矩阵 第五章 符号计算 5.1 符号对象和符号表达式 5.2 符号表达式和符号函数的操作 5.3 符号微积分 5.4 符号积分变换 5.5 符号代数方程的求解 5.6 符号微分方程的求解 5.7 利用MAPLE的深层符号计算资源 5.8 可视化数学分析界面 第六章 数据和函数的可视化 6.1 引导 6.2 二维曲线绘图的基本操作 6.3 三维绘图的基本操作 6.4 特殊图形和高维可视化 6.5 三维图形的精细控制 6.6 图像 6.7 图形窗的图形编辑功能 6.8 函数绘图的简捷指令 6.9 图形的打印和输出 第七章 M文件和面向对象编程 7.1 入门 7.2 M文本编辑器 7.3 MATLAB控制流 7.4 脚本文件和函数文件 7.5 变量的检测传递和限权使用函数 7.6 串演算函数 7.7 函数句柄 7.8 创建用户工具箱 7.9 调试和剖析 7.10 面向对象编程 第八章 SIMULINK交互式仿真集成环境 8.1 引导 8.2 模型的创建 8.3 边续系统建模 8.4 子系统的创建、装帧及受控执行 8.5 离散时间系统和混合系统 8.6 SIMULINK的分析工具 8.7 数值计算方面的考虑 8.8 S函数模块 第九章 句柄图形 9.1 句柄图形体系 9.2 图形对象的操作 9.3 对象属性的获取和设置 9.4 为低层指令绘图准备图/轴 9.5 图形窗的色彩资源和光标属性 9.6 轴对象 9.7 句柄图形应用专题 第十章 图形用户界面GUI制作 10.1 入门 10.2 图形用户界面的设计原则和一般步骤 10.3 界面菜单 uimenu) 10.4 用户控件 uicontrol) 10.5 由M函数文件产生用户菜单和控件 10.6 图形用户界面设计工具 第十一章 MATLAB编译器 11.1 编译器概述 11.2 编译器的安装和配置 11.3 MATLAB编译器使用入门 11.4 编译指令mcc简介 11.5 编译文件的性能优化 11.6 创建独立的外部应用程序 第十二章 应用程序接口API 12.1 C语言MEX文件的编写 12.2 MAT数据文件的应用 12.3 MATLAB引擎技术的应用 12.4 MATLAB中ActiveX技术的应用 12.5 MATLAB中DDE技术的应用 第十三章 Notebook 13.1 Notebook的安装 13.2 Notebook的启动 13.3 M-book模板的使用 13.4 科技演讲稿的制作 附录A:索引 附录B:光盘使用说明 参考文献

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值