【LeetCode刷题笔记】(六)状态压缩

91 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天,点击查看活动详情 这也是第40篇文章

前言

这篇文章探讨的话题是——状态压缩。

说实话,写这篇博文的时候我还是有一定压力的,因为我对状态压缩也不是特别了解,现在只能说是边写边总结,如果说得不准确的地方,还希望各位大佬多多指教哈~

什么是状态压缩

状态压缩常和动态规划一起出现使用。字面意思可以理解为“减少要讨论的状态情况”。

对我来说,它就和动态规划一样是个很抽象、很宽泛的概念,脱离具体题目去讨论它似乎没什么意义。用状态压缩更多是一种“直觉”,就是这道题它涉及到了状态的讨论,然后你觉得可以用一种更简便、时间复杂度更低的写法去写。

划分为k个相同的子集

题目

给定一个整数数组  nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。

思路

  • 思路关键词:状态压缩+动态规划
  • 对题意条件的分析:
    • 特殊情况:如果nums数组的和不能整除k,说明不行
    • 用 dp[i] 来表示在可用的数字状态为 i 的情况下是否可行,,dp数组z中的元素初始值都为true 。
    • 通过 自顶向下 的方式来进行求解原始状态的可行性
    • 当状态集合中不存在任何数字时,即 i=0 时,返回true

代码实现

class Solution {
    int[] nums;
    int per, n;
    boolean[] dp;

    public boolean canPartitionKSubsets(int[] nums, int k) {
        this.nums = nums;
        int all = Arrays.stream(nums).sum();
        if (all % k != 0) {
            return false;
        }
        per = all / k;
        Arrays.sort(nums);
        n = nums.length;
        if (nums[n - 1] > per) {
            return false;
        }
        dp = new boolean[1 << n];
        Arrays.fill(dp, true);
        return dfs((1 << n) - 1, 0);
    }

    public boolean dfs(int s, int p) {
        if (s == 0) {
            return true;
        }
        if (!dp[s]) {
            return dp[s];
        }
        dp[s] = false;
        for (int i = 0; i < n; i++) {
            if (nums[i] + p > per) {
                break;
            }
            if (((s >> i) & 1) != 0) {
                if (dfs(s ^ (1 << i), (p + nums[i]) % per)) {
                    return true;
                }
            }
        }
        return false;
    }
}