当前位置: 首页 > news >正文

数据结构与算法:子数组最大累加和问题及扩展

前言

子数组最大累加和问题看似简单,但能延伸出的题目非常多,千题千面,而且会和其他算法结合出现。

一、最大子数组和

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n=nums.size();
        vector<int>dp(n);//i位置往左能延伸出的最大累加和
        dp[0]=nums[0];
        int ans=dp[0];
        for(int i=1;i<n;i++)
        {
            dp[i]=max(nums[i],dp[i-1]+nums[i]);
            ans=max(ans,dp[i]);
        }

        return ans;
    }

    //扩展问题:
    //求子数组最大累加和的left、right和sum
    int left;
    int right;
    int sum;
    void extra(vector<int>&nums)
    {
        sum=INT_MIN;
        for(int l=0,r=0,pre=INT_MIN;r<nums.size();r++)
        {
            if(pre>=0)//前面累加和收益为正 -> 不换开头
            {
                pre+=nums[r];
            }
            else//换开头
            {
                pre=nums[r];
                l=r;
            }

            if(pre>sum)//更新
            {
                sum=pre;
                left=l;
                right=r;
            }
        }
    }
};

这个就是经典的子数组最大累加和问题,这个和扩展问题求left和right非常重要,是整个子数组最大累加和专题的基础!!

对于dp表的定义就是必须以i位置结尾,往左的子数组最大累加和。那么对于每个位置,都是当前的数加上要之前的和不要之前的两种情况。

那么如果要求这个子数组的left和right,整体过程类似滑动窗口,就是在空间压缩的基础上,如果之前的累加和大于等于零,说明收益是正的,那就保留之前的,否则就不要之前的,然后把l更新到r位置。当累加和能更新最大值,就连着left和right一起更新。

二、打家劫舍

class Solution {
public:
    int rob(vector<int>& nums) {
        int n=nums.size();
        if(n==1)
        {
            return nums[0];
        }
        if(n==2)
        {
            return max(nums[0],nums[1]);
        }

        vector<int>dp(n);
        dp[0]=nums[0];
        dp[1]=max(nums[0],nums[1]);
        for(int i=2;i<n;i++)
        {
            dp[i]=max(dp[i-1],max(nums[i],dp[i-2]+nums[i]));
            //dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
        }
        return dp[n-1];
    }
};

这个题首先要注意的就是特例,当只有一间或者两间的话直接返回。

之后就是分情况讨论,如果上间偷了,这间就不能偷,那么依赖的就是dp[i-1];如果上间没偷,那么就还有要之前的和不要之前的两种情况,最后取三种情况最大值即可。

其实因为这个题每间房子的收益都是大于零的,那么就不用讨论不要之前的这种情况了。

三、环形子数组的最大和

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int n=nums.size();

        int sum=nums[0],maxSum=nums[0],minSum=nums[0];
        int maxpre=nums[0],minpre=nums[0];
        for(int i=1;i<n;i++)
        {
            sum+=nums[i];//统计总累加和
            maxpre=max(nums[i],maxpre+nums[i]);
            maxSum=max(maxSum,maxpre);
            minpre=min(nums[i],minpre+nums[i]);
            minSum=min(minSum,minpre);
        }
        return sum==minSum?maxSum:max(maxSum,sum-minSum);//防止都扣掉
    }
};

这个题就需要点转化了,转化的思路就是,由于是环形数组,那么在其中删除任意一段子数组,剩下的仍连续,那么就可以将最大累加和转化成总体累加和减去最小的累加和和最大累加和的最大值。

整体的依赖关系还是要之前的和不要之前的,但这里要统计最大累加和和最小累加和。注意,若整体累加和等于最小累加和,为了防止把数全扣了,此时要返回最大累加和,否则就是最大累加和和相减后的最大值。

四、打家劫舍 II

class Solution {
public:
    int rob(vector<int>& nums) {
        int n=nums.size();
        if(n==1)
        {
            return nums[0];
        }

        vector<int>dp(n);
        //不偷第0间 -> 1~n-1
        dp[0]=0;
        dp[1]=nums[1];
        for(int i=2;i<=n-1;i++)
        {
            dp[i]=max(dp[i-1],max(nums[i],dp[i-2]+nums[i]));
        }
        int p1=dp[n-1];

        //偷第0间 -> nums[0]+2~n-2
        fill(dp.begin(),dp.end(),0);
        dp[0]=nums[0];
        dp[1]=nums[0];//注意!!!只偷第0间时dp[

相关文章:

  • 百度查询的ip与命令行输入 ipconfig 显示的IP地址有以下主要区别:
  • 管家婆财贸ERP BB102.采购销售订金管理
  • 快速生成mysql测试数据10w条
  • CSP/信奥赛C++中格式化输入输出scanf和printf的使用详解
  • 快速上手示例(以BEVFormer为例)
  • 【蓝桥杯】考前冲刺!
  • Unity中的静态合批使用整理
  • Oracle 数据库中,并行 DML
  • XSLFO XSLT:深入解析两种强大的XML转换技术
  • leetcode0069. x 的平方根-easy
  • 从零构建大语言模型全栈开发指南:第五部分:行业应用与前沿探索-5.1.2行业落地挑战:算力成本与数据隐私解决方案
  • 操作系统(二):实时系统介绍与实例分析
  • PM2 在 Node.js 项目中的使用与部署指南
  • 【力扣hot100题】(047)路径总和Ⅲ
  • 如何在Android中使用匿名内部类?
  • 人工智能混合编程实践:C++调用封装好的DLL进行图像超分重建
  • MinIO 入门指南:高性能对象存储的安装与使用
  • 国内使用Claude 3.7 sonnet的6种方法及Cursor+Claude3.7实现从原型到app开发
  • 034-QSharedMemory
  • 人工智能在医疗领域的创新应用与挑战