力扣刷题笔记2023.1.4

文章介绍了力扣每日一题1.4的解决方案,重点在于如何在给定约束条件下最大化数组中指定下标的值。文章提供了三种方法:1)贪心算法,从index处向两边遍历;2)贪心基础上结合二分查找降低时间复杂度;3)数学推导直接计算最优解。其中,贪心算法因时间复杂度问题被优化,而数学推导的方法实现了O(1)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

力扣刷题笔记

力扣每日一题1.4有界数组中指定下标的最大值



前言

提示:这里可以添加本文要记录的大概内容:

力扣每日一题1.4(有界数组中指定下标的最大值),从思考到各种解决方法。
题目:

给你三个正整数 n、index 和 maxSum 。你需要构造一个同时满足下述所有条件的数组 nums(下标 从 0 开始 计数):

  • nums.length == n
  • nums[i] 是 正整数 ,其中 0 <= i < n
  • abs(nums[i] - nums[i+1]) <= 1 ,其中 0 <= i < n-1
  • nums 中所有元素之和不超过 maxSum
  • nums[index] 的值被 最大化 返回你所构造的数组中的 nums[index] 。

注意:abs(x) 等于 x 的前提是 x >= 0 ;否则,abs(x) 等于 -x 。


一、题目分析

本题限制条件可理解为以下几个:

  1. 相邻元素值只有相同或者相邻这两种情况
  2. 元素的数量小于n
  3. 得到nums[index]为最大值
  4. 每个元素必须为正整数,即不能为0

二、解题方法

method1:贪心,从index下标处向两边遍历

即array[index]尽可能大,其他值尽可能小,从中间到两边逐渐递减(注意要求为正整数,所以最小值为1)

代码如下(示例):

class Solution {
public:
    int acquireTempMaxSum(int n,int index,int tempMax){
        int beforeIndexSum = 0;
        if(tempMax > index){
            int firstElementVal = tempMax - index;
            beforeIndexSum = ((tempMax + firstElementVal) * (index + 1))/2;
        }
        else{
            beforeIndexSum = (1 + tempMax)*tempMax /2 + (index - tempMax + 1);
        }
        cout << "aa " << beforeIndexSum << endl;
        int afterIndexSum = 0;
        if(tempMax >= (n - index)){
            int lastElementVal = tempMax - (n - index - 1);
            afterIndexSum = ((tempMax + lastElementVal)*(n - index))/2;
        }
        else{
            afterIndexSum = (1 + tempMax)*tempMax /2 + n - index - tempMax;
        }
        cout << "bb " << afterIndexSum << endl;
        int tempMaxSum = beforeIndexSum + afterIndexSum - tempMax;
        return tempMaxSum;
    }
    int maxValue(int n, int index, int maxSum) {
        int tempMax = 1;
        int tempMaxSum = acquireTempMaxSum(n,index,tempMax);
        while(tempMaxSum <=  maxSum){
            cout << tempMaxSum << endl;
            tempMax = tempMax + 1;
            tempMaxSum = acquireTempMaxSum(n,index,tempMax);
        }
        return tempMax - 1;
    }
};

本代码的arraqy[index]的选择是从1开始不断加1,直到不满足第三个条件(sum <= maxsum),或者从maxsum不断减1,直到满足第三个条件,从逻辑上来说没有错误,可以实现,但时间复杂度超时(为O(maxSum)),因此需要对该方法进行改进,超时测试用例如下图所示:
超时

method2:在method1贪心的基础上加上二分查找(降低时间复杂度)

即对于array[index]的取值不是从1开始累加(或从maxsum开始累减),而是左边界为1,右边界为maxsum,用二分查找查到满足条件的index,时间复杂度会降低到O(lg(maxsum))。

代码如下(示例):

class Solution {
public:
//强制转化符号(long)优先级大于运算符(+-*/)
    long acquireTempMaxSum(int n,int index,int tempMax){
        long beforeIndexSum = 0;
        if(tempMax > index){
            int firstElementVal = tempMax - index;
            beforeIndexSum = (long)(tempMax + firstElementVal)*(index + 1)/2;
        }
        else{
            beforeIndexSum = (long)(1 + tempMax)*tempMax /2 + (index - tempMax + 1);
        }
        //cout << "aa " << beforeIndexSum << endl;
        long  afterIndexSum = 0;
        if(tempMax >= (n - index)){
            int lastElementVal = tempMax - (n - index - 1);
            afterIndexSum = (long)(tempMax + lastElementVal)*(n - index)/2;
        }
        else{
            afterIndexSum = (long)(1 + tempMax)*tempMax /2 + n - index - tempMax;
        }
       // cout << "bb " << afterIndexSum << endl;
        long tempMaxSum = beforeIndexSum + afterIndexSum - tempMax;
        return tempMaxSum;
    }
    int maxValue(int n, int index, int maxSum) {
        int tempMax;
        int left = 1;
        int right = maxSum;
        while(left < right){
            tempMax = (left + right) /2 + 1;
            long tempMaxSum = acquireTempMaxSum(n,index,tempMax);
            //cout << "///// "  << tempMaxSum << endl;
            if(tempMaxSum <=  maxSum){
                left = tempMax;
            }
            else{
                right = tempMax - 1;
            }
        }
        return left;
    }
};

注意:本题的测试用例比较大,接近(int)的上线,因此很多步骤都需要进行强制转换,把int类型转化成long类型,否则会溢出,出现tempMaxSum值为负值的情况。改进后的贪心算法可以成功通过所有测试用例。

method3:数学推导

一边的元素和情况总共只有两种

  1. 记nums[index]为big,它离数组的某个边界距离为length,当big<= length+1时,还未到达边界,元素值就已降为1,并保持1直到边界,此时元素和为(1+big-1)(big-1)/2 + length - (big-1),合并之后为(big^2-3big)/2 + length + 1.。
  2. 若big > length+1时,则元素和为(big - 1 + big -length) * length / 2,合并后即(2 * big - 1 - length) * length / 2

数组之和由三部分组成,nums[index],index左边的数组元素之和,index右边的数组元素之和。左边的元素个数为left=index,右边元素个数为right = n - 1 - index。根据对称性,设left < right,则根据big由小到大可以用三种情况来表示:

  1. big <= left + 1,numsSum = (big^2-3big)/2 + left + 1 + big + (big^2-3big)/2 + right + 1,此时left + 1 >= big > 0,若big取最大值left + 1时,numsSum >= maxSum,则可证明,big必在此区间中,所以此时可合并成big^2 - 2*big + left + right + 2 - numsSum = 0,numsSum赋值为maxSum解该一元二次方程,向下取整,即可得到big在此种情况下的解。
  2. right + 1 >= big >= left + 1,则numsSum = (2 * big - left - 1)left / 2+ big + (big^2-3big)/2 + right + 1,同理判断big= right+1的上界,如若大于等于maxSum,则将numsSum = maxSum,求该一元二次方程,合并后该一元二次方程为0=big^2/2 + (left - 1/2)*big + right+ 1-maxSum-(left^2 + left) / 2,可求出big的值。
  3. 若前两种情况均不满足,则big > right + 1,此时numsSum = (2 * big - left - 1)left / 2 + big + (2 * big - right - 1)right / 2,求解方程即可,big = (2maxSum + left^2 + left +right^2 + right)/(2(left + right +1))。
    代码如下所示
class Solution {
public:
    int maxValue(int n, int index, int maxSum) {
        double left = index;
        double right = n - index - 1;
        if (left > right) {
            double temp = left;
            left = right;
            right = temp;
        }

        double upper = ((double) (left + 1) * (left + 1) - 3 * (left + 1)) / 2 + left + 1 + (left + 1) + ((left + 1) * (left + 1) - 3 * (left + 1)) / 2 + right + 1;
        if (upper >= maxSum) {
            double a = 1;
            double b = -2;
            double c = left + right + 2 - maxSum;
            return (int) floor((-b + sqrt(b * b - 4 * a * c)) / (2 * a));
        }

        upper = ((double) 2 * (right + 1) - left - 1) * left / 2 + (right + 1) + ((right + 1) * (right + 1) - 3 * (right + 1)) / 2 + right + 1;
        if (upper >= maxSum) {
            double a = 1.0 / 2;
            double b = left + 1 - 3.0 / 2;
            double c = right + 1 + (-left - 1) * left / 2 - maxSum;
            return (int) floor((-b + sqrt(b * b - 4 * a * c)) / (2 * a));
        } else {
            double a = left + right + 1;;
            double b = (-left * left - left - right * right - right) / 2 - maxSum;
            return (int) floor(-b / a);
        }
    }
};

时间复杂度为O(1)


总结

本题主要三种解法,但其实主要的解题思路还是贪心算法。第一种是纯粹的贪心算法,method2是为了优化时间复杂度,在贪心算法的基础上增加了二分查找。method3比较难想到,根据数学公式的推导,从而进一步降低时间复杂度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值