java多个数组求和代码_(Trivial)LeetCode 1354——多次求和构造目标数组

关键字:贪心算法,优先队列,multiset

归航return:LeetCode 1610—可见点的最大数目​zhuanlan.zhihu.com
归航return:LeetCode LCP 05——发 LeetCoin​zhuanlan.zhihu.com

Problem

Given an array of integers target. From a starting array, A consisting of all 1's, you may perform the following procedure :

  • let x be the sum of all elements currently in your array.
  • choose index i, such that 0 <= i < target.size and set the value of A at index i to x.
  • You may repeat this procedure as many times as needed.

Return True if it is possible to construct the target array from A otherwise return False.

Example 1:

Input: target = [9,3,5]
Output: true
Explanation: Start with [1, 1, 1] 
[1, 1, 1], sum = 3 choose index 1
[1, 3, 1], sum = 5 choose index 2
[1, 3, 5], sum = 9 choose index 0
[9, 3, 5] Done

Example 2:

Input: target = [1,1,1,2]
Output: false
Explanation: Impossible to create target array from [1,1,1,1].

Example 3:

Input: target = [8,5]
Output: true

Constraints:

  • N == target.length
  • 1 <= target.length <= 5 * 10^4
  • 1 <= target[i] <= 10^9
1354. 多次求和构造目标数组 - 力扣(LeetCode)​leetcode-cn.com

Solution

这道题目的例子就给了我们提示:每次选出 target 中数组最大的元素当作加出来以后的结果,然后反过来执行步骤,直到得到初始数组或者遇到了错误。具体来说,反过来执行应该这么做:首先找到最大的元素,然后将这个元素减去其他元素的和,就得到了上一步中这个位置的元素。遇到了错误的情况也是很好想到的——我们要保证最大的元素减去其他元素的和之后仍然是正整数,因此最大的元素必须大于所有其他元素的和,否则这说明上一步的状态根本不合理。

因此,上述要求就需要我们维护一个有序的数据结构,保证能很快得到最大元素,同时维护求和。显然求和是容易维护的,只需要一个额外变量,并在每次修改之后做减法即可,因此实际上就是要一个有序的数据结构。那么答案也就呼之欲出了,两种可能:优先队列,自平衡二叉搜索树。由于题目中的数组元素可能出现重复元素,因此这里要求我们的自平衡二叉搜索树允许重复元素,这里需要使用 C++ 的专属数据结构:std::multiset。最后的代码中,C++ 将使用 std::multiset,Java 将使用优先队列 PriorityQueue

好的,我们直接按照这个思路写出 C++ 代码:

typedef unsigned long long ull;
class Solution {
public:
    bool isPossible(vector<int>& target) {
        if (target.size() == 1){
            return target[0] == 1;
        }
        multiset<ull> targetSorted(target.begin(), target.end());
        ull sum = accumulate(target.begin(), target.end(), static_cast<ull>(0));
        while (true){
            ull maxNum = *targetSorted.rbegin();
            ull otherNumSum = sum - maxNum;
            if (maxNum <= otherNumSum){
                return false;
            }
            sum -= otherNumSum;
            maxNum -= otherNumSum;
            targetSorted.erase(--targetSorted.end());
            targetSorted.insert(maxNum);
            if (*targetSorted.rbegin() == 1){
                return true;
            }
        }
    }
};

注:这里用了 C++ 的反向迭代器,方便地访问 multiset 中的最大值。Java 类似做法则是 set.last()

这里的 accumulate 库函数来源于标准库 <numeric>,文档在这里。然而很不幸,在 [1,1000000000] 这个 test case 上超时了。究其原因,是因为我们在这里每次都要重复减去最大的元素,而实际上,如果最大的元素减去其他元素之和后还是最大的元素,那么我们仍然用这个元素减掉。为此我们可以定义一个 rate 变量,代表最大元素除以其他元素的和的比例,如果比值大于 2,我们直接减去其他元素的和的比值减一倍,保证每次修改之后,最大元素的和都不超过其他元素的和太多,这样就可以大幅节约时间了。修改后的代码如下:

typedef unsigned long long ull;
class Solution {
public:
    bool isPossible(vector<int>& target) {
        if (target.size() == 1){
            return target[0] == 1;
        }
        multiset<ull> targetSorted(target.begin(), target.end());
        ull sum = accumulate(target.begin(), target.end(), static_cast<ull>(0));
        while (true){
            ull maxNum = *targetSorted.rbegin();
            ull otherNumSum = sum - maxNum;
            if (maxNum <= otherNumSum){
                return false;
            }
            ull rate = maxNum / otherNumSum;
            if (rate > 2){
                sum -= (rate - 1) * otherNumSum;
                maxNum -= (rate - 1) * otherNumSum;
            }
            else{
                sum -= otherNumSum;
                maxNum -= otherNumSum;
            }
            targetSorted.erase(--targetSorted.end());
            targetSorted.insert(maxNum);
            if (*targetSorted.rbegin() == 1){
                return true;
            }
        }
    }
};

Java 中没有类似于 C++ 中这样的特殊数据结构 std::multiset,但我们只关心最大值,因此可以使用优先队列来完成这一事项,由于 Java 中的优先队列是默认小根堆,因此这里还需要设定排序顺序,将比较顺序改成相反的顺序。代码如下:

class Solution {
    public boolean isPossible(int[] target) {
        if (target.length == 1){
            return target[0] == 1;
        }
        long sum = 0;
        PriorityQueue<Long> pq = new PriorityQueue<>(Comparator.reverseOrder());
        for (long x : target){
            pq.offer(x);
            sum += x;
        }
        while (true){
            long maxNum = pq.poll();
            long otherNumSum = sum - maxNum;
            if (maxNum <= otherNumSum){
                return false;
            }
            long rate = maxNum / otherNumSum;
            maxNum -= Math.max(rate - 1, 1L) * otherNumSum;
            sum -= Math.max(rate - 1, 1L) * otherNumSum;
            pq.offer(maxNum);
            if (pq.peek() == 1){
                return true;
            }
        }
    }
}

时间复杂度上,每次求和都会减少一半的求和结果,因此时间复杂度是

,空间上我们用了一个额外数组,因此空间复杂度则是

EOF。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值