洛谷题解P1115 最大子段和 暨 P1714 切蛋糕

P1115原题传送门

P1714原题传送门

( ext{Solution - P1117})

一道 DP 题目。

  • 状态的表示 : 令 (f[i]) 表示 以 (f[i]) 结尾的最大子段和。
  • 初始化 : (f[i] = 0)
  • 状态的转移 : (f[i] = max(f[i-1]+a[i],a[i]))

但,对于状态的转移,我们可以发现数据范围

对于 (100\%) 的数据,保证 (1 leq n leq 2 imes 10^5 , -10^4 leq a_i leq 10^4)

明确标注了数列中可能存在负数,这一点在测试点 (2) 中也得到了完美的体现。(话说全是负数)

当数列中存在负数时,(f[n]) 不一定是 (max)

故,还需再在求完 (f[i]) 后求最大值并记录答案。

( ext{Code})

#include<iostream>
#include<cstdio>
using namespace std;
const int Maxn=2e5+10;
inline void read(int &x){
	int f=1;
	char ch=getchar();
	x=0;
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
	x*=f;
} 
int n;
int a[Maxn];
int dp[Maxn];
inline int max(int a,int b){return a>b?a:b;}
int main(){
	int ans=1<<31;      //int max-> 1<<31-1,1<<31<0
	read(n);
	for(int i=1;i<=n;i++){
		read(a[i]);
		dp[i]=max(dp[i-1]+a[i],a[i]);
		ans=max(dp[i],ans);
	}
	printf("%d",ans);
	return 0;
}

( ext{Solution - P1117 & P1714})

这两道题都可以用一种数据结构来解决。(双倍经验)
这两道题都是最大区间的题
所以可以维护一个前缀和单调递增的单调队列。
基本和滑动窗口相似,只不过不需要维护一个 (p) 数组来记录队列中元素的值。

( ext{Code-P1714})( ext{P1115}) 代码相似,请读者自行修改)

#include<iostream>
#include<cstdio>
using namespace std;
const int Maxn=5e5+10;
inline void read(int &x){
	int f=1;
	char ch=getchar();
	x=0;
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+(ch&15);
		ch=getchar();
	}
	x*=f;
}
int n,m;
int p;
int sum[Maxn];
int q[Maxn];
int main(){
	read(n);read(m);
	for(int i=1;i<=n;i++){
		read(p);
		sum[i]=sum[i-1]+p;
	}
	int l=1,r=1;	//队列中默认有元素了,所以 r=1  
	int ans=1<<31;	
	for(int i=1;i<=n;i++){
		while(l<=r&&q[l]<i-m) l++;	//过时出队 
		ans=max(ans,sum[i]-sum[q[l]]);	//sum[i]-sum[q[l]] -> 通过维护前缀和数组求得当前区间和 
		while(l<=r&&sum[i]<=sum[q[r]]) r--;	//维护单调递增 
		q[++r]=i;
	}
	printf("%d",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/-pwl/p/13864882.html