首页 > 其他分享 >1681. 最小不兼容性

1681. 最小不兼容性

时间:2022-10-26 17:48:42浏览次数:46  
标签:兼容性 sub mask 最小 value 1681 枚举 子集

题目描述

给一个数组nums,数组里面有一些值,值可能会重复
再给一个k,需要把nums分配到k个子集中,每个子集没有相同元素,且所有子集的不兼容性之和最小?

f1-枚举子集+状态压缩+动态规划

基本分析

  1. 明显不满足要求的有哪些?某个值的次数>k
  2. 是不是存在贪心的思路?没有
  3. 因为需要给每个子集放n//k个元素,这个要怎么放才能考虑比较好转移?用mask表示nums被选择的状态,sub是我们最后一个选择的子集,尝试枚举子集sub(需要满足1的个数是n//k的条件, 需要满足元素不重要求)。对每个sub计算不兼容性v[sub],同时看mask^sub是不是存在,存在时f[mask^sub] + value[sub]就是一个潜在的值;多个sub对应的以上结果取min就是f[mask]
  4. 根据以上的转移思路,需要做哪些预处理?预处理出所有满足条件的sub对应的不相容值value[sub];具体怎么实现?
    • 在1<<n范围内进行遍历,找到1个个数是n//k的sub
    • 在n的范围内遍历sub的1处的索引,追加索引对应的值到集合,如果集合重复,这个sub不满足要求;如果不重复,遍历完索引后,在更新将对应的不兼容性存在value[sub]中
  5. 定义dp结构和初始化边界注意什么?可能很多mask是不能满足要求的,这里吧f定义成了字典;当mask不选的时候,不兼容值是0,f[0]=0
  6. dp过程的思路?
    • 还是在1<<n范围内遍历mask,对mask的要求是1的个数需要是n//k的倍数,不满足的直接跳过
    • 为了减少枚举的复杂度,在mask的子集比value长度少的时候选择枚举mask子集转移;当mask子集长度比value长时候,枚举value的k和v进行转移
      • 前者的时候,先找出mask的子集sub,sub初值时mask,如果要转移需要满足sub在value中且mask^sub在f中,如果满足条件,说明是一种合理方式,可以用来转移mask,到底小不小再说;不管能不能成立,继续便利sub直到sub=0
      • 后者的时候,直接枚举value的键-值:sub-v。对sub的要求是sub是mask的子集(mask&sub=sub), 且mask^sub能转移;后面步骤和以上类似
  7. 最后的结果?f[(1<<n)-1]是不是存在

代码

class Solution:
    def minimumIncompatibility(self, nums: List[int], k: int) -> int:
        n = len(nums)
        if n == k:
            return 0
        most = Counter(nums).most_common()[0][1]
        if most > k:
            return -1
        
        # 预处理1个数为n//k子集对应的不兼容性
        value = dict()
        for sub in range(1<<n):
            if bin(sub).count('1') != n//k:
                continue
            flag = True
            freq = set()
            for j in range(n):
                if sub & (1<<j):
                    if nums[j] in freq:
                        flag = False
                        break
                    freq.add(nums[j])
            if flag:
                value[sub] = max(freq) - min(freq)
        
        # 处理dp
        f = dict()
        f[0] = 0
        for mask in range(1, 1<<n):
            if bin(mask).count('1') % (n//k) != 0:
                continue
            if 2**(bin(mask).count('1')) < len(value):
                sub = mask
                while sub > 0:
                    if sub in value and mask ^ sub in f:
                        if mask not in f:
                            f[mask] = f[mask ^ sub] + value[sub]
                        else:
                            f[mask] = min(f[mask], f[mask ^ sub] + value[sub])
                    sub = (sub-1) & mask
            else:
                for sub, v in value.items():
                    if sub & mask == sub and mask ^ sub in f:
                        if mask not in f:
                            f[mask] = f[mask^sub] + value[sub]
                        else:
                            f[mask] = min(f[mask], f[mask^sub]+value[sub])
        
        return f[(1<<n)-1] if (1<<n)-1 in f else -1

复杂度

时间:\(枚举子集合需要O(3^n)\)
空间:\(O(2^n)\)

总结

这个题目的特点是当前数组中的数字的选择与否可以用二进制的mask表示,但是比较难想到怎么去实现转移?这个时候我们就要去考虑用一个mask的子集sub表示选择的最后一个子集的选择情况。因为题目中给了要求,这个sub就要满足(个数+不重的),对某个mask和其中一个合理的sub,f[mask^sub]+value[sub]就一种可能的转移,最后是不是最小,还要结合多个可能的sub去看。
为什么需要考虑预处理?在转移的时候去找value[sub]就可能太晚了,而且先处理完把结果写成字典,后面直接用就行。考虑怎么存结果?就是常见的枚举sub,对sub的长度和值的选择做删选,满足条件的把对应的不兼容性存下来
转移时候的思路也比较常见,就是遍历mask,不考虑不满足1长度要求的mask。对mask考虑转移的时候,需要枚举子集sub,需要看能不能从之前的状态f[mask^sub]转移,所以对sub和mask^sub都有要求。另外考虑复杂度,对mask子集少的时候,考虑的是枚举mask的子集,对子集多的时候,考虑的是枚举value。

标签:兼容性,sub,mask,最小,value,1681,枚举,子集
From: https://www.cnblogs.com/zk-icewall/p/16829269.html

相关文章

  • 求构成正方形的最小代价
    题目描述牛牛有4根木棍,长度分别为a,b,c,d。羊羊家提供改变木棍长度的服务,如果牛牛支付一个硬币就可以让一根木棍的长度加一或者减一。牛牛需要用这四根木棍拼凑一个正方形......
  • 多次排序减去摸一个值,求平方和最小值
    题目描述有一种有趣的字符串价值计算方式:统计字符串中每种字符出现的次数,然后求所有字符次数的平方和作为字符串的价值例如:字符串"abacaba",里面包括4个'a',2个'b',1个......
  • 代码随想录day21 | 530.二叉搜索树的最小绝对差 501. 二叉搜索树中的众数 236. 二叉
    530.二叉搜索树的最小绝对差题目|文章思路二叉搜索树的特点是按照中序遍历从小到大进行排列,因此,按照中序遍历,逐个比较即可找到最小差值进行中序遍历,当前节点和前一个......
  • 746 使用最小花费爬楼梯
    题目746使用最小花费爬楼梯给你一个整数数组cost,其中cost[i]是从楼梯第i个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以......
  • JeecgBoot低代码开发平台与达梦数据完成兼容性互认证
    近日,JeecgBoot与达梦数据库管理系统V8完成兼容性认证测试;通过双方共同测试表明,Jeecgboot低代码开发平台与达梦数据库管理系统V8,相互兼容,系统功能运行稳定,能够满足用户更多......
  • 859 Kruskal求最小生成树
    把所有边按照权重值从小到大排序,然后一一收入集合,利用联通集判断一条边的两个点是否在一个联通集中如果在就不收录这条边#include<bits/stdc++.h>usingnamespacestd;......
  • POJ 2110(最小生成树)
    这题的思路就是找一个范围,看看这个范围是否可行主流是二分Ans,我是先把点排序,求最小生成树检查首位的ProgramP2110;typeed=recordu,v,w:longint;end;vara......
  • 调色盘 (3维k点最小最远点对-容斥原理)
    调色盘(pastele)题目描述Albus得到了一份礼物:来自Polaris的水彩油墨包。Polaris的油墨包里面有N个颜色,现在Albus打算选其中的K种来作一幅风景画。既然是风景画,颜色就不能太......
  • BZOJ 1185([HNOI2007]最小矩形覆盖-旋转卡壳+点集几何意义)
    1185:[HNOI2007]最小矩形覆盖TimeLimit: 10Sec  MemoryLimit: 162MBSec  SpecialJudgeSubmit: 258  Solved: 137Description l要事先改成......
  • 最小斯坦纳树
    最小斯坦纳树斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种。最小生成树是在给定的点集和边中寻求最短网络使所有点连通。而最小斯坦纳树允许在给定点......