leetcode
leetcode 2301 ~ 2350
统计 K-Free 子集的总数

统计 K-Free 子集的总数

难度:

标签:

题目描述

代码结果

运行时间: 46 ms, 内存: 16.1 MB


/*
 * 思路:
 * 1. 创建一个函数用于判断一个子集是否为K-Free。
 * 2. 使用流生成所有可能的子集并过滤掉不满足条件的子集。
 * 3. 对过滤后的子集计数。
 */

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Solution {
    public long countKFreeSubsets(int[] nums, int k) {
        List<List<Integer>> subsets = IntStream.range(0, 1 << nums.length)
            .mapToObj(i -> IntStream.range(0, nums.length)
                .filter(j -> (i & (1 << j)) != 0)
                .mapToObj(j -> nums[j])
                .collect(Collectors.toList()))
            .collect(Collectors.toList());
        return subsets.stream()
            .filter(subset -> isKFree(subset, k))
            .count();
    }

    private boolean isKFree(List<Integer> subset, int k) {
        for (int i = 0; i < subset.size(); i++) {
            for (int j = i + 1; j < subset.size(); j++) {
                if (Math.abs(subset.get(i) - subset.get(j)) == k) {
                    return false;
                }
            }
        }
        return true;
    }
}

解释

方法:

题解的思路是将数组中的数字根据它们除以k的余数进行分类,存储在字典中,其中键是余数,值是具有相同余数的数字列表。对于每个余数类别,使用动态规划计算可能的子集数量。动态规划数组dp记录了当前长度的子序列可以形成的'k-free'子集数量。如果两个相邻数字之差等于k,则该子集可以选择包含或不包含前一个数字,否则可以自由选择包含任何前面的数字。最后,所有余数类别的结果相乘得到最终答案。

时间复杂度:

O(n^2)

空间复杂度:

O(n)

代码细节讲解

🦆
题解中提到将数组进行排序,排序后的数组是如何帮助进行余数分类和后续的动态规划计算的?
排序数组主要有两个作用:首先,它能帮助以统一的方式将数组中的元素按照它们除以k的余数进行分类,确保同一分类中的元素聚集在一起,便于后续处理。其次,排序保证了每个分类中的元素按照自然顺序排列,这对于动态规划计算尤为重要,因为在制定状态转移方程时,我们需要考虑元素之间的相对位置和大小关系。通过对元素进行排序,可以直接通过索引顺序来比较元素,简化了动态规划的实现,尤其是在处理元素间差值等于k的情况时更加直观和易于实现。
🦆
在动态规划的过程中,为什么当两个相邻数字之差等于k时,状态转移方程会变化?这样的变化是基于什么样的逻辑或假设?
在动态规划中,当两个相邻数字之差等于k时,状态转移方程会变化,是因为这种情况下,根据题目定义的'k-free'子集的要求,这两个数字不能同时出现在同一个子集中。因此,对于当前数字,我们在计算其包含在子集中的可能性时,不能简单地考虑包括前一个数字的所有情况,而是需要排除掉同时包含前一个数字的情况。这就导致了状态转移方程的变化:我们需要从前一个状态(不包括前一个数字的所有子集情况)和再前一个状态(考虑更早的数字,但不包括前一个数字的可能性)来进行状态的更新。这种变化确保了我们构建的子集满足'k-free'的条件,即子集中任何两个元素的差不应该是k。
🦆
题解中提到的动态规划数组dp的每个元素dp[j]代表的是什么具体含义?如何解释dp数组中每个值的计算过程?
动态规划数组dp[j]中的每个元素代表的是考虑到第j个元素时,可以形成的所有可能的'k-free'子集的数量。在初始化阶段,dp[0]设置为2,表示第一个元素可以选择包含或不包含,即存在两种情况:一个是空集,一个是只包含这一个元素的集合。对于后续的每个元素,其dp[j]的值基于前面元素的dp值计算得出。如果当前元素与前一个元素的差不等于k,意味着可以自由选择是否包含该元素,因此状态转移为dp[j] = 2 * dp[j-1],即每种前一个状态都可以选择包含或不包含当前元素。如果差等于k,则需要特殊处理,从而确保不违反'k-free'的条件,此时dp[j]的值会考虑不包含前一个元素的所有情况,因此根据前面的状态来适当减少可能的子集数量。这样的计算过程确保了每一步都严格符合题目要求,避免了非法子集的生成。

相关问题