Daily算法刷题【面试经典150题-6️⃣kadane/】

文章目录

  • 🍊kadane
    • 1.1最大子数组和(dp/前缀和🐱)
    • [1.2 环形子数组的最大和](#1.2 环形子数组的最大和)
      • [变形1) 环状最大两段子段和(dp)](#变形1) 环状最大两段子段和(dp))
      • [变形2) 环状最大两段子段和](#变形2) 环状最大两段子段和)

🍊kadane

1.1最大子数组和(dp/前缀和🐱)

cpp 复制代码
/*
    法1:动态规划
    dp[i]代表 以nums[i]结尾的连续子数组的最大和
    时间复杂度:o(N)
*/
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp(n);
        dp[0] = nums[0];
        for(int i = 1; i < n; i++){
            if(dp[i-1]>0) dp[i] = dp[i-1] + nums[i];
            else dp[i] = nums[i];
        }
        int ans = dp[0];
        for(int i = 1; i < n; i++){
            ans = max(ans,dp[i]);
        }
        return ans;
        
    }
};

法2:前缀和,🐱

cpp 复制代码
/*
    前缀和:
    要求连续最大子数组和:比如i-j sum[j] - sum[i-1]
    其实就是。找出一个最大差值 sum[j] - sum[i-1]

*/
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int maxSum = INT_MIN; 
        int minprevSum = 0; //最小的前缀和
        int curSum = 0;
        int n = nums.size();
        for(int i = 0; i < n; i++){
            curSum += nums[i];
            maxSum = max(maxSum,curSum - minprevSum);
            minprevSum = min(minprevSum, curSum);
        }
        return maxSum;
    }
};

1.2 环形子数组的最大和

cpp 复制代码
/*
    直接将数组扩充一倍,想到了但是不会处理, 直接dp会导致某些数选两遍 是不对的
    原来可以转换为两种情况:
        (1)最大子数组不是环状的,其实就和上一题一样了
        (2)这个子数组一部分在头,一部分在尾部,这种情况是重点
        xxmmxx
        mxm
        最后的max就是数组综合剪掉中间的部分

        注意:如果最小子数组就是整个数组的和,第二种情况就是0
        如果此时maxSum>0会取maxSum
        ❕但是如果maxSum<0就会取0,这是不对的,此时答案应该为maxSum
*/
class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int sum = 0, maxSum = nums[0], curMax = 0;
        int minSum = nums[0], curMin = 0;
        for(int x : nums){
            curMax = max(curMax + x, x);
            maxSum = max(curMax,maxSum);
            curMin = min(curMin + x, x);
            minSum = min(curMin, minSum);
            sum += x;
        }
        if(maxSum <= 0) return maxSum;
        else return max(maxSum, sum - minSum);
    }
};

变形1) 环状最大两段子段和(dp)

link

题目描述

给出一段长度为 n n n 的环状序列 a a a,即认为 a 1 a_1 a1 和 a n a_n an 是相邻的,选出其中连续不重叠且非空的两段使得这两段和最大。

输入格式

第一行是一个整数 n n n,表示序列的长度。

第二行有 n n n 个整数,描述序列 a a a,第 i i i 个数字表示 a i a_i ai。

输出格式

一行一个整数,为最大的两段子段和是多少。

输入输出样例 #1

输入 #1

复制代码
7
2 -4 3 -1 2 -4 3

输出 #1

复制代码
9

说明/提示

数据规模与约定

对于全部的测试点,保证 2 ≤ n ≤ 2 × 10 5 2 \leq n \leq 2 \times 10^5 2≤n≤2×105, − 10 4 ≤ a i ≤ 10 4 -10^4 \leq a_i \leq 10^4 −104≤ai≤104。

dp:预处理,

preMax代表前i个数的最大子段和

postMax代表后i个数的最大子段和

然后最后枚举i,直接o(n)求出来结果

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
long long x[1000010],preMax[1000010],postMax[1000010];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>x[i];
    preMax[1]=x[1];postMax[n]=x[n];
    for(int i = 2; i <= n; i++){
        preMax[i] = max(preMax[i-1] + x[i], x[i]); // 以当前节点为结尾的最大子段和
    }
    for(int i = 2; i <= n; i++){
        preMax[i] = max(preMax[i-1], preMax[i]); // 不一定以i为结尾,i之前的,更新最大值
    }
    for(int i = n-1; i >= 1; i--){
        postMax[i] = max(postMax[i+1] + x[i], x[i]); // 以当前节点为结尾的最大子段和
    }
    for(int i = n-1; i >= 1; i--){
        postMax[i] = max(postMax[i+1], postMax[i]); // 不一定以i为结尾,i之后的,更新最大值
    }
	long long ans= preMax[1]+postMax[3];
	for(int i=3;i<n;i++){
		ans=max(ans,preMax[i-1]+postMax[i+1]);
    }
	cout<<ans;
	return 0;
}

变形2) 环状最大两段子段和

题目描述

给出一段长度为 n n n 的环状序列 a a a,即认为 a 1 a_1 a1 和 a n a_n an 是相邻的,选出其中连续不重叠且非空的两段使得这两段和最大。

输入格式

第一行是一个整数 n n n,表示序列的长度。

第二行有 n n n 个整数,描述序列 a a a,第 i i i 个数字表示 a i a_i ai。

输出格式

一行一个整数,为最大的两段子段和是多少。

输入输出样例 #1

输入 #1

复制代码
7
2 -4 3 -1 2 -4 3

输出 #1

复制代码
9

说明/提示

数据规模与约定

对于全部的测试点,保证 2 ≤ n ≤ 2 × 10 5 2 \leq n \leq 2 \times 10^5 2≤n≤2×105, − 10 4 ≤ a i ≤ 10 4 -10^4 \leq a_i \leq 10^4 −104≤ai≤104。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
long long x[1000010],preMax[1000010],postMax[1000010];
long long preMin[1000010],postMin[1000010];
int main(){
	int n;
	cin>>n;
    long long sum = 0;
	for(int i=1;i<=n;i++){
		cin>>x[i];
		sum+=x[i];
    }
    preMax[1]=x[1];postMax[n]=x[n];
    preMin[1]=x[1];postMin[n]=x[n];
    for(int i = 2; i <= n; i++){
        preMax[i] = max(preMax[i-1] + x[i], x[i]); // 以当前节点为结尾的最大子段和
        preMin[i] = min(preMin[i-1] + x[i], x[i]); // 以当前节点为结尾的最小子段和
    }
    for(int i = 2; i <= n; i++){
        preMax[i] = max(preMax[i-1], preMax[i]); // 不一定以i为结尾,i之前的,更新最大值
        preMin[i] = min(preMin[i-1], preMin[i]); // 不一定以i为结尾,i之前的,更新最小值
    }
    for(int i = n-1; i >= 1; i--){
        postMax[i] = max(postMax[i+1] + x[i], x[i]); // 以当前节点为结尾的最大子段和
        postMin[i] = min(postMin[i+1] + x[i], x[i]); // 以当前节点为结尾的最小子段和
    }
    for(int i = n-1; i >= 1; i--){
        postMax[i] = max(postMax[i+1], postMax[i]); // 不一定以i为结尾,i之后的,更新最大值
        postMin[i] = min(postMin[i+1], postMin[i]); // 不一定以i为结尾,i之后的,更新最小值
    }
	long long ans= preMax[1]+postMax[2];
	for(int i=2;i<n;i++){
		ans=max(ans,preMax[i-1]+postMax[i+1]);
    }
    long long res = 0;
    
    long long ans2= preMin[1]+postMin[2];
    for(int i=2;i<n;i++){
		ans2=min(ans2,preMin[i-1]+postMin[i+1]);
    }
    if(ans < 0) res = ans;
    else{
        res = max(ans, sum - ans2);
    }
    cout<<res;
	return 0;
}
相关推荐
薯片锅巴2 小时前
代码随想录算法训练营第四十四天:孤岛计数(广搜版),孤岛计数(深搜版),最大岛屿的面积
算法
海清河晏1112 小时前
Linux进阶篇:深入理解线程
java·jvm·算法
yong99902 小时前
基于压缩感知与后向投影算法的合成孔径雷达成像实现
算法
蒙奇D索大2 小时前
【数据结构】排序算法精讲 | 插入排序全解:稳定性、复杂度与实战代码剖析
数据结构·算法·排序算法
刘永鑫Adam2 小时前
Nature Methods | 诸奇赟组-Scikit-bio:用于生物组学数据分析的基础Python库
人工智能·python·算法·机器学习·数据分析
hweiyu002 小时前
查找算法:分类及特点
算法·分类
youngee112 小时前
hot100-51搜索二维矩阵
数据结构·算法·矩阵
byzh_rc2 小时前
[模式识别-从入门到入土] 支持向量积SVM
数据库·人工智能·算法
im_AMBER2 小时前
Leetcode 86 【二分查找】在排序数组中查找元素的第一个和最后一个位置
笔记·学习·算法·leetcode