<—点个赞吧QwQ
宣传一下算法提高课整理{:target=”_blank”}
输入一个长度为 $n$ 的整数序列,从中找出一段长度不超过 $m$ 的连续子序列,使得子序列中所有数的和最大。
注意: 子序列的长度至少是 $1$。
输入格式
第一行输入两个整数 $n,m$。
第二行输入 $n$ 个数,代表长度为 $n$ 的整数序列。
同一行数之间用空格隔开。
输出格式
输出一个整数,代表该序列的最大子序和。
数据范围
$1 \\le n,m \\le 300000$
输入样例:
6 4
1 -3 5 1 -2 3
输出样例:
7
思路
闫氏$\text{DP}$分析法:
状态表示:$f_{i}$
- 集合:以$i$为结尾的最大子序和
- 属性:$\max$
状态计算:
- $f_i=\max\limits_{j\in[1,i - 1]}\{s_i-s_j\}$
- 变化一下式子,得$f_i=s_i-\min\limits_{j\in[0,i-1]}\{s_j\}$
- 所以很明显我们可以维护一个$1\sim i-1$的单调队列来优化
时间复杂度
单调队列时间复杂度为$O(n)$,总时间复杂度为$O(n)$
代码
#include <iostream>
#include <deque>
using namespace std;
typedef long long LL;
const int N = 300010;
int n,m;
int a[N];
LL s[N];
deque <int> q;
int main () {
scanf ("%d%d",&n,&m);
for (int i = 1;i <= n;i++) {
scanf ("%d",&a[i]);
s[i] = s[i - 1] + a[i];
}
LL ans = -1e14;
q.push_back (0);
for (int i = 1;i <= n;i++) {
while (q.size () && i - q.front () > m) q.pop_front ();
ans = max (ans,s[i] - s[q.front ()]);
while (q.size () && s[q.back ()] >= s[i]) q.pop_back ();
q.push_back (i);
}
printf ("%d\n",ans);
return 0;
}
### tt = 0时 AC
### tt = -1时 WA
tt==-1时会覆盖s[0]的情况,也就是说没有了 $a_1+a_2+..+a_i$
模拟了一下完全理解了,感谢
不用谢
状态计算的j取值范围是不是错了, 从0开始吧
错了,已修正
应该是从max(0,i-m)开始吧
还要考虑[1,m-1]的答案啊