几道比较经典的子数组OJ题

x33g5p2x  于2022-05-22 转载在 其他  
字(4.3k)|赞(0)|评价(0)|浏览(307)

一.最大子数组和

二. 连续子数组的最大值II

三.子矩阵最大累加和

四.打家劫舍

五.乘积最大的子数组

六.乘积为正数最长子数组

一.最大子数组和

1.对应letecode链接
53. 最大子数组和 - 力扣(LeetCode)

2.题目描述:

3.解题思路⭐⭐⭐

在之前的博客中我们提到了子数组问题的解法:一般子数组问题就是以某个位置开头怎么样,或者以某个位置结尾怎么样。在本题中我们可以这样想必须以i位置结尾的情况下最大累加和是多少。每个位置都这么搞最大累加和一定就在其中,那么每个位置的最大累计和又该如何求得?其实非常的简单必须以i位置结尾情况下的最大累加和有两种情况:

情况一:只要i位置的数字因为i位置前面的数可能都是负数

情况二:要必须以i-1位置结尾的情况下的累加和+当前位置的累加和。

这样我们就可以得到一个dp方程:dp[n] = Max(dp[n - 1] + arr[n], arr[n]);

对应动图:

4.对应代码:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int N=nums.size();
        vector<int>dp(N);
          dp[0]=nums[0];
          //dp[i]的含义为必须以i位置结尾的情况下最大累加和是多少
        int maxSum=nums[0];//记录最大的累加和
        for(int i=1;i<N;i++){

            dp[i]=max(dp[i-1]+nums[i],nums[i]);
            maxSum=max(maxSum,dp[i]);
        }
        return maxSum;
    }
};

优化版本:数组压缩

如果有某一段子数组的累加和小于0了那么取得最大累加和的子数组一定不会包含它。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
          
          int sum=0;
          int maxSum=nums[0];
          for(int i=0;i<nums.size();i++){
              sum+=nums[i];
              maxSum=max(sum,maxSum);
              sum=sum<0?0:sum;
          }
          return maxSum;
    }
};

二. 连续子数组的最大值II

1.对应OJ链接
连续子数组的最大和(二)_牛客题霸_牛客网 (nowcoder.com)

2.题目描述:

3.解题思路:

本题思路和上面那题基本一模一样,只不过本题需要记录最长的那个子数组是谁。在这里我们只需要定义开头位置的变量和结尾位置的变量,将这两个变量维护好即可。

4.对应代码:

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param array int整型vector 
     * @return int整型vector
     */
    vector<int> FindGreatestSumOfSubArray(vector<int>& array) {
        // write code here
        int start=0;
        int tmpStart=0;//记录开始位置和结束位置
        int end=0;
        int tmpEnd=0;
        int sum=array[0];
        int maxSum=array[0];
        for(int i=1;i<array.size();i++){
        if(sum>=0){
            sum+=array[i];
            tmpEnd=i;
        }
        else{
          sum=array[i];
           tmpEnd=i;
           tmpStart=i;
        }
            if(sum>=maxSum){
                maxSum=sum;
                end=tmpEnd;
                start=tmpStart;
            }
          }
        vector<int>ans(end-start+1);
       
        for(int i=0;i<ans.size();i++){
            ans[i]=array[i+start];
        }
        return ans;
        }
};

三.子矩阵最大累加和

1.对应OJ链接
子矩阵的最大累加和问题_牛客题霸_牛客网 (nowcoder.com)

2.题目描述:

3.解题思路
本题其实上上面那题的升级版:同样的我们可以这样搞必须以0行做地基且必须以0行做地基子矩阵的最大累加和是多少,必须以0~1两行做地基且必须以0~1两行做地基子矩阵的最大累加和是多少。我们依次枚举答案一定就在其中。在这里0~1两行我们可以使用数组压缩在用上面那题的模板即可具体请看代码:

4.对应代码:

#include<iostream>
#include<limits.h>
#include<vector>
using namespace std;
int main()
{
    int M,N;
    cin>>M>>N;
    
 vector<vector<int>>matix(M,vector<int>(N));
    for(int i=0;i<M;i++){
        for(int j=0;j<N;j++){
            cin>>matix[i][j];
        }
    }
   
    int maxSum=INT_MIN;
    for(int i=0;i<M;i++){
          vector<int>sum(matix[0].size());
        for(int j=i;j<M;j++){//i.....j范围做地基的最大累加和
            int cur=0;
            for(int k=0;k<N;k++){
                sum[k]+=matix[j][k];//数组压缩
                cur+=sum[k];
                maxSum=max(maxSum,cur);
                cur=cur<0?0:cur;
            }
            
        }
      

    }
      cout<<maxSum<<endl;
    
    return 0;
}

四.打家劫舍

1.对应letecode链接
198. 打家劫舍 - 力扣(LeetCode)

2.对应题目描述:

3.解题思路:

本题和上面那题的思想是一样的同样是对应i位置怎么样。

dp[i]表示0-i能偷的最大金额,dp[i]由三种情况中的最大值转移过来
1.dp[i - 2] + nums[i] 表示偷当前位置,那么i-1的位置不能偷,而且需要加上dp[i-2],也就是前i-2个房间的金钱
2.dp[i - 1]表示不偷当前位置,只偷i-1的房间。

3.只偷当前位置的数

4.对应代码:

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

五.乘积最大的子数组

1.对应letecode链接
152. 乘积最大子数组 - 力扣(LeetCode)

2.题目描述:

3.解题思路:

本题依然是子数组问题:子数组问题一般都是以谁谁结尾怎么样或者开头怎么样。在这里我们依然是考虑每个位置结尾的情况怎么样,在这里我们求必须以每个位置结尾的情况下最大乘积是多少?我们从0下标一直枚举到最后那么答案一定就在其中 。由于负数和负数乘起来有可能变成正数,所以我们需要定义两个变量来记录最小乘积和最大乘积。

4.对应代码:

class Solution {
public:
    int maxProduct(vector<int>& nums) {
              int Maxans=nums[0];//记录全局的最大乘积
              int maxval=nums[0];
              int minval=nums[0];
              //求子数组必须以i为位置结尾的情况下最大的答案是多少
              for(int i=1;i<nums.size();i++)
              {
                int maxEnd=maxval*nums[i];
                int minEnd=minval*nums[i];
                 maxval=max(max(maxEnd,minEnd),nums[i]);
                 minval=min(min(maxEnd,minEnd),nums[i]);
                 Maxans=max(Maxans,maxval);
                  
              }
              return Maxans;
    }
};

六.乘积为正数最长子数组

1.对应letecode链接
1567. 乘积为正数的最长子数组长度 - 力扣(LeetCode)

2.题目描述:

3.解题思路:

本题和上题一样同样的是子数组问题:同样的是以某个位置结尾如何如何。用两个数组记录到当前位置乘积为正数的最长子数组的长度和乘积为负数的最长子数组长度,每到一个位置考虑当前位置的正负和前一个位置的最长子数组的关系 。具体请看代码 

4.对应代码:

class Solution {
public:
    int getMaxLen(vector<int>& nums) {
        if(nums.size()==1){
        
            return nums[0]<=0?0:1;
        }
            int N=nums.size();
          vector<int>negetive(N);
          vector<int>positive(N);
          negetive[0]=nums[0]<0?1:0;
          positive[0]=nums[0]>0?1:0;
          //negetive[i]代表的是[0.....i]范围内乘积为负数的最长长度
          //positive[i]代表的是[0....i]范围上乘积为正数的最长长度
          int maxLen=0;
          
          for(int i=1;i<N;i++){
              
              if(nums[i]<0){
                  positive[i]=negetive[i-1]==0?0:negetive[i-1]+1;
                  negetive[i]=positive[i-1]+1;
              }

              else if(nums[i]==0){
                 positive[i]=0;
                 negetive[i]=0;
              }

              else{
                  positive[i]=positive[i-1]+1;
                  negetive[i]=negetive[i-1]==0?0:negetive[i-1]+1;
              }
              maxLen=max(maxLen,positive[i]);
          }
          return maxLen;
    }
};

相关文章