加载中...
1982-从子集的和还原数组(Find Array Given Subset Sums)
发表于:2021-12-03 | 分类: 困难
字数统计: 1.5k | 阅读时长: 6分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/find-array-given-subset-sums

英文原文

You are given an integer n representing the length of an unknown array that you are trying to recover. You are also given an array sums containing the values of all 2n subset sums of the unknown array (in no particular order).

Return the array ans of length n representing the unknown array. If multiple answers exist, return any of them.

An array sub is a subset of an array arr if sub can be obtained from arr by deleting some (possibly zero or all) elements of arr. The sum of the elements in sub is one possible subset sum of arr. The sum of an empty array is considered to be 0.

Note: Test cases are generated such that there will always be at least one correct answer.

 

Example 1:

Input: n = 3, sums = [-3,-2,-1,0,0,1,2,3]
Output: [1,2,-3]
Explanation: [1,2,-3] is able to achieve the given subset sums:
- []: sum is 0
- [1]: sum is 1
- [2]: sum is 2
- [1,2]: sum is 3
- [-3]: sum is -3
- [1,-3]: sum is -2
- [2,-3]: sum is -1
- [1,2,-3]: sum is 0
Note that any permutation of [1,2,-3] and also any permutation of [-1,-2,3] will also be accepted.

Example 2:

Input: n = 2, sums = [0,0,0,0]
Output: [0,0]
Explanation: The only correct answer is [0,0].

Example 3:

Input: n = 4, sums = [0,0,5,5,4,-1,4,9,9,-1,4,3,4,8,3,8]
Output: [0,-1,4,5]
Explanation: [0,-1,4,5] is able to achieve the given subset sums.

 

Constraints:

  • 1 <= n <= 15
  • sums.length == 2n
  • -104 <= sums[i] <= 104

中文题目

存在一个未知数组需要你进行还原,给你一个整数 n 表示该数组的长度。另给你一个数组 sums ,由未知数组中全部 2n子集的和 组成(子集中的元素没有特定的顺序)。

返回一个长度为 n 的数组 ans 表示还原得到的未知数组。如果存在 多种 答案,只需返回其中 任意一个

如果可以由数组 arr 删除部分元素(也可能不删除或全删除)得到数组 sub ,那么数组 sub 就是数组 arr 的一个 子集sub 的元素之和就是 arr 的一个 子集的和 。一个空数组的元素之和为 0

注意:生成的测试用例将保证至少存在一个正确答案。

 

示例 1:

输入:n = 3, sums = [-3,-2,-1,0,0,1,2,3]
输出:[1,2,-3]
解释:[1,2,-3] 能够满足给出的子集的和:
- []:和是 0
- [1]:和是 1
- [2]:和是 2
- [1,2]:和是 3
- [-3]:和是 -3
- [1,-3]:和是 -2
- [2,-3]:和是 -1
- [1,2,-3]:和是 0
注意,[1,2,-3] 的任何排列和 [-1,-2,3] 的任何排列都会被视作正确答案。

示例 2:

输入:n = 2, sums = [0,0,0,0]
输出:[0,0]
解释:唯一的正确答案是 [0,0] 。

示例 3:

输入:n = 4, sums = [0,0,5,5,4,-1,4,9,9,-1,4,3,4,8,3,8]
输出:[0,-1,4,5]
解释:[0,-1,4,5] 能够满足给出的子集的和。

 

提示:

  • 1 <= n <= 15
  • sums.length == 2n
  • -104 <= sums[i] <= 104

通过代码

高赞题解

本题的简化版(sums 中所有元素均为非负数)是一道非常经典的题目。本题在原题基础上进行了拓展,是一道兼具思考性和趣味性的好题。

所有元素均为非负数

该限制条件下可以用归纳法求解。在下述解法中,“从 multiset 里去除元素 $x$”指的是去除一个 $x$,而不是去除所有 $x$。

  1. 设 $\mathbb{S}$ 表示 sums 中所有元素组成的 multiset。
  2. 首先将 $0$ 从 $\mathbb{S}$ 中去除,此时 $\mathbb{S}$ 中的最小值即为 ans 中的最小值。
  3. 若我们已经推出了 ans 中最小的 $k$ 个元素,那么我们从 $\mathbb{S}$ 中把这 $k$ 个元素所有子集的和去除。此时 $\mathbb{S}$ 中的最小值即为 ans 中的第 $(k + 1)$ 小值。

复杂度 $\mathcal{O}(2^nn)$。

所有元素可以为任意整数

解法

  1. 令 $m < 0$ 表示 $\mathbb{S}$ 中的最小值,将 $\mathbb{S}$ 中所有元素增加 $-m$ 变成另一个 multiset $\mathbb{S’}$。
  2. 对 $\mathbb{S’}$ 按 sums 中所有元素均为非负数的方法求解,得到答案 tmp
  3. 寻找 tmp 中的任意一个子集,使得该子集的和等于 $m$。把子集中的所有元素变为相反数(乘以 $-1$)后得到的 ans 即为最终答案。

复杂度仍为 复杂度 $\mathcal{O}(2^nn)$。

证明

令 $\mathbb{A}$ 表示“标准答案”的 multiset,令 $\mathbb{A}^-$ 表示 $\mathbb{A}$ 中所有负数组成的 multiset,显然有 $\sum \mathbb{A}^- = m$。

观察 $\mathbb{T} \subseteq \mathbb{A}$,可以发现 $\sum \mathbb{T} - m = \sum (\mathbb{T} \oplus \mathbb{A}^-)$,其中 $\oplus$ 是这样一种集合运算:若 $\mathbb{A}^-$ 中的一个元素 $a$ 也存在于 $\mathbb{T}$ 中,则将其从 $\mathbb{T}$ 中去除;否则将 $-a$ 加入 $\mathbb{T}$。因此原问题和转化后的问题的子集具有一一对应的映射关系,其中一个问题有解,另一个问题也有解。

代码

class Solution {
public:
    vector<int> recoverArray(int n, vector<int>& sums) {
        int BIAS = 0;
        for (int x : sums) BIAS = min(BIAS, x);
        BIAS = -BIAS;

        multiset<int> st;
        for (int x : sums) st.insert(x + BIAS);

        st.erase(st.begin());
        vector<int> ans;
        ans.push_back(*st.begin());

        for (int i = 1; i < n; i++) {
            for (int msk = 0; msk < (1 << i); msk++) if (msk >> (i - 1) & 1) {
                int sm = 0;
                for (int j = 0; j < i; j++) if (msk >> j & 1) sm += ans[j];
                st.erase(st.find(sm));
            }
            ans.push_back(*st.begin());
        }

        for (int i = 0; i < (1 << n); i++) {
            int sm = 0;
            for (int j = 0; j < n; j++) if (i >> j & 1) sm += ans[j];
            if (sm == BIAS) {
                for (int j = 0; j < n; j++) if (i >> j & 1) ans[j] = -ans[j];
                break;
            }
        }
        return ans;
    }
};

统计信息

通过次数 提交次数 AC比率
887 1907 46.5%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言
上一篇:
1980-找出不同的二进制字符串(Find Unique Binary String)
下一篇:
1981-最小化目标值与所选元素的差(Minimize the Difference Between Target and Chosen Elements)
本文目录
本文目录