洛谷题解P1115 最大子段和 暨 P1714 切蛋糕
P1115原题传送门
P1714原题传送门
\(\text{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 \times 10^5\ ,\ -10^4 \leq a_i \leq 10^4\)
明确标注了数列中可能存在负数,这一点在测试点 \(2\) 中也得到了完美的体现。(话说全是负数)
当数列中存在负数时,\(f[n]\) 不一定是 \(\max\)
故,还需再在求完 \(f[i]\) 后求最大值并记录答案。
\(\text{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;
}
\(\text{Solution - P1117 & P1714}\)
这两道题都可以用一种数据结构来解决。(双倍经验)
这两道题都是最大区间的题
所以可以维护一个前缀和单调递增的单调队列。
基本和滑动窗口相似,只不过不需要维护一个 \(p\) 数组来记录队列中元素的值。
\(\text{Code-P1714}\)(\(\text{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;
}
本文欢迎转载,转载时请注明本文链接