数组的子集能否累加出K

简介: 数组的子集能否累加出K

前言

给定一个有正、有负、有0的数组arr,
给定一个整数k,
返回arr的子集是否能累加出k
1)正常怎么做?
2)如果arr中的数值很大,但是arr的长度不大,怎么做?

题目一

子集就是子序列
每一个位置的数要跟不要展开生成的子序列就对应一个子集.
标准的从左往右的尝试模型

dpi:
arr 0~i范围的数自由选择能不能累加出j这个累加和来,是一 张true, false表,分情况讨论:
1)完全不用i位置的数, dpli-1
2)-定要含有i位置的数, dp[i- 1]lj-arr[i]

在这里插入图片描述
求dp5

1)0~4搞出12
2)用5位置的3, 0~4搞出9

列的大小不能按K的大小来

在这里插入图片描述
如果arr里只有正数,列值搞到K= 10就够了

在这里插入图片描述
难点在于有正、有负、有0,只定义0~K是不够的,有负数列的大小不能够按照K的大小来决定
把所有负数累加到一起,所有正数累加到一起 这样累加和可能范围就定下来了
在这里插入图片描述
平移对应
想象中dpi的值实际要拿dpi的值

代码

    public static boolean isSum3(int[] arr, int sum) {
        if (sum == 0) {
            return true;
        }
        // sum != 0
        if (arr == null || arr.length == 0) {
            return false;
        }
        int min = 0;
        int max = 0;
        for (int num : arr) {
            min += num < 0 ? num : 0;
            max += num > 0 ? num : 0;
        }
        // min~max
        if (sum < min || sum > max) {
            return false;
        }

        // min <= sum <= max
        int N = arr.length;
        boolean[][] dp = new boolean[N][max - min + 1];
        // dp[0][0] = true
        dp[0][-min] = true;
        dp[0][arr[0] - min] = true;
        for (int i = 1; i < N; i++) {
            for (int j = min; j <= max; j++) {
                dp[i][j - min] = dp[i - 1][j - min];
                int next = j - min - arr[i];
                dp[i][j - min] |= (next >= 0 && next <= max - min && dp[i - 1][next]);
            }
        }
        return dp[N - 1][sum - min];
    }

问题二解决思路

第二问使用的是分治思想
在这里插入图片描述
这个表列需要从最小值一直推到最大值如果arr中值很大,会导致列非常多,
整张表有可能算不完

在这里插入图片描述

假设arr长度40,如果不切成左右两半2^40,程序是跑不完的
分左右两半,每边20个数跑暴力每个数要跟不要都展开(2^20),收集所有累加和
左边20个数暴力的方式跑出来100万个累加和,
右边20个数暴力的方式跑出来100万个累加和,
问arr 40个数任意选你能不能搞定某个累加和17怎么算?
如果单独左边,右边可以搞定17就返回T,否则
左右两侧凑,想一个整合逻辑
遍历左边所有可能的累加和一百万个。但是当我一旦确定一个累加和的时候,
我在右边找他所伴随的累加和是非常快的。
比如左边3,右边凑14
0(1)
    public static boolean isSum(int[] arr, int sum) {
        if (sum == 0) {
            return true;
        }
        if (arr == null || arr.length == 0) {
            return false;
        }
        if (arr.length == 1) {
            return arr[0] == sum;
        }
        int N = arr.length;
        int mid = N >> 1;
        HashSet<Integer> leftSum = new HashSet<>();
        HashSet<Integer> rightSum = new HashSet<>();
        process4(arr, 0, mid, 0, leftSum);
        process4(arr, mid, N, 0, rightSum);
        // 单独查看,只使用左部分,能不能搞出sum
        // 单独查看,只使用右部分,能不能搞出sum
        // 左+右,联合能不能搞出sum
        // 左部分搞出所有累加和的时候,包含左部分一个数也没有,这种情况的,leftsum表里,0
        for (int l : leftSum) {
            if (rightSum.contains(sum - l)) {
                return true;
            }
        }
        return false;
    }
    // arr[0...i-1]决定已经做完了!形成的累加和是pre
    // arr[i...end - 1] end(终止)  所有数字随意选择,
    // arr[0...end-1]所有可能的累加和存到ans里去
    public static void process4(int[] arr, int i, int end, int pre, HashSet<Integer> ans) {
        if (i == end) {
            ans.add(pre);
        } else {
            process4(arr, i + 1, end, pre, ans);
            process4(arr, i + 1, end, pre + arr[i], ans);
        }
    }
相关文章
|
2月前
|
算法 测试技术 PHP
通过阶乘获取一个一维PHP数组中全部的组合情况
在PHP中,`FactorialHandler`类通过递归实现了数组的全组合,`getArrAllCombineByFactor`方法逐个添加元素至所有子组合中。示例代码展示了一个包含组合生成的过程,输出了所有可能的排列。此外,还提供了一个深度优先搜索的递归函数`depth_picker`,用于生成数组的所有组合,从选择单个元素到选择所有元素。这两个示例揭示了递归在处理数组组合问题中的应用,常见于算法设计和测试用例生成。
20 0
通过阶乘获取一个一维PHP数组中全部的组合情况
|
4月前
|
算法 测试技术 C#
【线段树 区间位运算模板】3117划分数组得到最小的值之和
【线段树 区间位运算模板】3117划分数组得到最小的值之和
|
4月前
|
Java 测试技术
统计满足条件的子集个数
统计满足条件的子集个数
37 0
|
4月前
|
存储 算法 程序员
【算法训练-数组 一】【数组子集】:最长无重复子数组
【算法训练-数组 一】【数组子集】:最长无重复子数组
34 0
LeetCode-2044 统计按位或能得到最大值子集的数目
LeetCode-2044 统计按位或能得到最大值子集的数目
|
算法
算法篇之二分查找(第74题探索二维矩阵、第287题寻找重复数)
算法篇之二分查找(第74题探索二维矩阵、第287题寻找重复数)
103 0
|
C++
计算一个数组的子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
55 0
数组与矩阵——3.数组中重复的数字
数组与矩阵——3.数组中重复的数字