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;
}