题目链接:​​https://www.luogu.com.cn/problem/P2627​

解题思路:

定义状态 \(f[i]\) 表示:"前 \(i\) 只奶牛,且当 \(i < n\) 时,第 \(i+1\) 只奶牛不选"的情况下的最大值。

则,当 \(i \le k\) 时,

\[f[i] = \sum_{j=1}^{i} e[j] \]

当 \(i > t\) 时,

\[f[i] = \max_{j \in [i-k-1, i-1]}(f[j] + \sum_{x=j+2}^i e[x]) \]

其中区间和我们可以额外开一个 sum 数组用于存储前缀和。

实现代码如下(TLE代码):

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, k;
long long e[maxn], sum[maxn], f[maxn], ans;
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i ++) {
cin >> e[i];
sum[i] = sum[i-1] + e[i];
}
if (k > n) k = n;
for (int i = 1; i <= k; i ++) f[i] = sum[i];
for (int i = k+1; i <= n; i ++)
for (int j = 1; j <= k; j ++)
f[i] = max(f[i], f[i-j-1] + sum[i] - sum[i-j]);
for (int i = 1; i <= n; i ++)
ans = max(ans, f[i]);
cout << ans << endl;
return 0;
}

但是这样的时间复杂度是 \(O(n \cdot k)\) 的,会超时。

于是考虑将上述的状态转移方程进行一下转化:

\[f[i] = \max_{j \in [i-k-1, i-1]}(f[j] - sum[j+1]) + sum[i] \]

所以考虑将 \(f[j] - sum[j+1]\) 放到一个单调队列当中。

实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, k;
long long e[maxn], sum[maxn], f[maxn], ans;
deque<int> que;
long long cal(int i) {
return f[i] - sum[i+1];
}
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i ++) {
cin >> e[i];
sum[i] = sum[i-1] + e[i];
}
if (k > n) k = n;
for (int i = 1; i <= k; i ++) f[i] = sum[i];
int j = 0;
for (int i = k+1; i <= n; i ++) {
for (; j < i; j ++) {
while (!que.empty() && cal(que.back()) <= cal(j)) que.pop_back();
que.push_back(j);
}
while (que.front() < i-k-1) que.pop_front();
f[i] = f[que.front()] - sum[que.front()+1] + sum[i];
}
for (int i = 1; i <= n; i ++)
ans = max(ans, f[i]);
cout << ans << endl;
return 0;
}

然后我错了样例3,因为我一开始将 \(j\) 的初始坐标设为了 \(1\) ,后来改成了 \(0\) 就 AC 了。

可以拿下面这组测试数据试一下:

测试in

6 1
4
7
2
0
8
9

测试out

16