最大子段和___O(n) —— 递推dp

    给定 n n n 个整数(可能为负数)组成的序列 a [ 1 ] , a [ 2 ] , a [ 3 ] , … , a [ n ] a[1],a[2],a[3],…,a[n] a[1],a[2],a[3],,a[n],求该序列如 a [ i ] + a [ i + 1 ] + … + a [ j ] a[i]+a[i+1]+…+a[j] a[i]+a[i+1]++a[j]的子段和的最大值。当所给的整数均为负数时,定义子段和为 0 0 0

输入样例:

6
-2 11 -4 13 -5 -2

输出样例:

20

    思路:很明显用 O ( n 2 ) O(n^2) O(n2)可以直接求解,同时也能用 O ( n l o g n ) O(nlogn) O(nlogn)的分治方法来解决,方法是将这个序列对半分,然后最大子段和只会出现在左边序列、右边序列、跨越中间点的序列,可以解决

    当然,最好的办法还是动态规划,思路可以顺推,既然只需要遍历一遍,在遍历的过程中我们只需要判断将 a [ i ] a[i] a[i]连到 a [ i − 1 ] a[i-1] a[i1]上,或者重新从 i i i 开始
    那么,我们定义一个缓冲值,计算从开始那个点到当前这个点的上一个点 的字段和,若 > 0 >0 >0,则加上当前这个点会得到更大的值,反之就重新设置起点
    所以我们甚至不需要用到dp数组,只需要设置一个dp变量,记录到当前点的最大值

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, a[10005], dp[10005];
int ans, k;

int main(){
	scanf("%d", &n);
	for(int i=0; i<n; i++) scanf("%d", a+i);
	for(int i=0; i<n; i++){
		if(k>0) k += a[i];
		else k=a[i];
		
		if(i) dp[i] = max(dp[i-1], k);
	}
	printf("%d\n", dp[n-1]);
}

更新一波 当所给的整数均为负数时,仍然求最大字段和
这里用到单调队列来处理,时间复杂度:O(n)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int n, a[maxn], pre[maxn], q[maxn];

int main(){
	scanf("%d", &n);
	for(int i=1; i<=n; i++) scanf("%d", a+i);
	int head = 1, tail = 0, ans = -1e9; q[++tail] = 0;
	for(int i=1; i<=n; i++) pre[i] = pre[i-1] + a[i];
	for(int i=1; i<=n; i++){
		ans = max(ans, pre[i] - pre[q[head]]);
		while(head<=tail && pre[i]<=pre[q[tail]]) tail--;
		q[++tail] = i;
	}
	printf("%d\n", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值