LeetCode每日一题 1681. 最小不兼容性

1681. 最小不兼容性

题意

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

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

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

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

样例输入

nums = [1,2,1,4], k = 2

样例输出

4

提示

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

c++代码

class Solution {
public:
    int minimumIncompatibility(vector<int>& nums, int k) {
        int n = nums.size();
        int group = n / k;
        vector<int> dp((1 << n), INT_MAX);
        dp[0] = 0;
        unordered_map<int, int> values;
        for(int mask = 1; mask < (1 << n); mask++ ){
            if(__builtin_popcount(mask) != group) continue;
            int mx = INT_MIN, mn = INT_MAX;
            unordered_set<int> cur;
            for(int i = 0; i < n; i++){
                if(mask & (1 << i)){
                    if(cur.count(nums[i]) > 0) break;
                    cur.insert(nums[i]);
                    mx = max(mx, nums[i]);
                    mn = min(mn, nums[i]);
                }
                if(cur.size() == group) values[mask] = mx-mn;
            }
        }

        for(int mask = 0; mask < (1 << n); mask++ ){
            if(dp[mask] == INT_MAX) continue;
            unordered_map<int, int> seen;
            int sub = 0;
            for(int i = 0; i < n; i++ ){
                if((mask & (1 << i)) == 0){
                    seen[nums[i]] = i;
                }
            }
            for(auto& i : seen){
                sub = sub | (1 << i.second);
            }
            int next = sub;
            while(next > 0){
                if(values.count(next) != 0){
                    dp[mask | next] = min(dp[mask | next], dp[mask] + values[next]);
                }
                next = (next - 1) & sub;
            }
        }

        return dp[(1 << n) - 1] != INT_MAX ? dp[(1 << n) - 1] : -1;
    }
};

python代码

from typing import *
class Solution:
    def minimumIncompatibility(self, nums: List[int], k: int) -> int:
        n = len(nums);
        group = n // k;
        dp = [inf] * (1 << n)
        dp[0] = 0
        values = {}

        for mask in range(1 << n):
            if mask.bit_count() != group:
                continue
            mx = -inf
            mn = inf
            cur = set()
            for i in range(n):
                if (mask & (1 << i)) > 0:
                    if nums[i] in cur:
                        break
                    cur.add(nums[i])
                    mx = max(mx, nums[i])
                    mn = min(mn, nums[i])
            if len(cur) == group: values[mask] = mx - mn
        
        for mask in range(1 << n):
            if dp[mask] == inf:
                continue
            seen = {}
            for i in range(n):
                if (mask & (1 << i)) == 0:
                    seen[nums[i]] = i
            if len(seen) < group: continue
            sub = 0
            for i in seen:
                sub = sub | (1 << seen[i])
            nxt = sub
            while nxt > 0:
                if nxt in values:
                    dp[mask | nxt] = min(dp[mask | nxt], dp[mask] + values[nxt])
                nxt = (nxt - 1) & sub
            
        return dp[-1] if dp[-1] < inf else -1

备注

好难一题,总体流程如下。首先用二进制码是否为1/0来标记数组中的元素是否存在集合中,再然后对数据做预处理,找到所有可能的集合并记录其不兼容性。然后从mask为0遍历所有子串,转移方程为dp[mask|sub] = min(dp[mask|sub], dp[mask] + values[sub])

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值