leetcode 2834.找出美丽数组的最小和

这道题作者一开始用的暴力解法,效果还不错,但是对于特别大的数据是过不去的。

先讲一下我暴力的思路:作者用了双指针的解法,我们可以先定义一个数组,就是把数组从1开始考虑,n个连续整数输入到里面去。

然后定义两个指针left和right,一个指向第一个元素,一个指向最后一个元素,然后让这两个数相加。如果nums[left]+nums[right]>target,我们就把right往后移动;如果nums[left]+nums[right]<target,那么就把left往前移(因为我们的初始化数组是有顺序的)。遇到==target的情况,我们需要更换数据:

由于题目中要求我们最小和,我们需要把nums[left]和nums[right]之中比较大的数字更换。那么更换成什么呢?我们知道,数组里面不能出现重复的数字,所以我们只能将这个数字变大,而不能变小,因为变小可能就会引起数组的元素重复了,并且数字变大需要变大到比这个数组的最大元素要大1,这样才能保证最小和。

那么怎么移动呢?这就是语言基础了,我们直接找到需要更改的元素位置,然后将这个元素后面的整体移动覆盖它,再在最后面更换上那个更新后的数字,这样既能保证有序,又能保证最小和(其实这里的思路具有贪心的思想,证明的话不能完全给出)。

这一道题跟某一次leetcode周赛上的题很像,但那个题是可以暴力解出来的,这个题由于数据过大,有10个样例没通过,原因就是时间超出限制。时间复杂度都已经到O(logn)2了,这没办法,思路就到这里了。

class Solution {
public:
    int minimumPossibleSum(int n, int target) {
        vector<int>nums(n+1,0);
        for(int i=0;i<n;i++){
            nums[i]=i+1;
        }
        int left=0;
        int right=n-1;
        int st=0;
        int index=0;
        while(left<right){
            if(nums[left]+nums[right]>target)
            right--;
            else if(nums[left]+nums[right]<target)
            left++;
            else{
                index=nums[n-1];
                if(right==n-1)
                nums[n-1]=index+1;
                else{
                    st=right;
                    for(int i=st;i<n;i++)
                    nums[i]=nums[i+1];
                    nums[n-1]=index+1;
                }
                left=0;
                right=n-1;
            }
        }
        int sum=0;
        for(int i=0;i<n;i++){
            sum=(sum%1000000007+nums[i]%1000000007)%1000000007;
        }
    return sum;
    }
};

至于数学的做法,应该会很好想一点:

以target为基准,我们直接可以判断:1,target-1

2,target-2

...

一直到target/2这里,target/2是直接可以选择的,没必要关乎什么奇数偶数。

前面的那些列举,我们只能选择一个去用,那我们就只选择小的就可以。

这样的话,我们其实已经完成了一部分的答案了,设m=min(target/2,n),显然,在target/2之前的那m个数字我们直接用等差数列的公式计算出来就行了也就是m*(m+1)/2。

至于剩下的那n-m个数,我们知道,刚刚已经从小于等于target的范围里选过了,所以之后需要选择比target大的那一部分了。因此,我们再次使用等差数列的公式就行了:

也就是(n-m)*(target+(target+(n-m)-1)/2,简化一下也就是(n-m)*(2*target+n-m-1)/2。

注意:有些人可能不懂target+(n-m)-1这一部分是啥,因为target后面的部分都是连续的,也就是target+1,target+2...一直加到n-m-1的时候才停止,加上target本身一共n-m个。这个式子就是这样来的。

上代码:

class Solution {
public:
    int minimumPossibleSum(int n, int target) {
        long long m=min(target/2,n);
        return (m*(m+1)+(n-m-1+2*target)*(n-m))/2%1000000007;
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值