修剪草坪 / Mowing the Lawn G
题目链接:ybt金牌导航1-4-3 / luogu P2627
题目大意
有一个序列,你要在里面选一个序列,使得不会有超过 k 个数是在原序列中相邻的。
思路
我们先看到在序列中选要的好像没有什么思路,那我们考虑选哪些是不选的。
那我们就会发现我们可以进行 DP。
设
f
i
f_i
fi 为前
i
i
i 个数,满足题目的要求,且第
i
i
i 个不选,不选的数的和的最小值。
那容易得出转移方程:(
f
0
=
0
f_0=0
f0=0 显然)
f
i
=
e
i
+
min
j
=
max
{
i
−
k
−
1
,
0
}
i
−
1
f
j
f_i=e_i+\min\limits_{j=\max\{i-k-1,0\}}^{i-1}f_j
fi=ei+j=max{i−k−1,0}mini−1fj
那你可以发现它这个 min \min min 可以用单调队列来维护,然后就好了。
代码
#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
int n, k, l, r, a[100001];
ll e[100001], f[100001], sum, ans = 1e15;
int main() {
scanf("%d %d", &n, &k);
for (int i = 1; i <= n; i++) scanf("%lld", &e[i]), sum += e[i];
k++;
f[1] = e[1];
l = 1;
r = 1;
for (int i = 1; i <= n; i++) {//单调队列
while (a[l] < i - k && l <= r) l++;
f[i] = f[a[l]] + e[i];//dp转移
while (f[i] <= f[a[r]] && l <= r) r--;
a[++r] = i;
}
for (int i = max(0, n - k + 1); i <= n; i++)
ans = min(ans, f[i]);//枚举最后一个不选的位置在哪里
printf("%lld", sum - ans);
return 0;
}