この質問はとても面倒なようですが、一見したところ、まったくアイデアがありませんでした。
しかし、トピックを注意深く分析すると、次のことがわかります。
- c = 1 c = 1 c=1の場合、答えは0ですが、そのような点はないようです。
- c> nc> n c>>nの場合、答えはシーケンスの合計です。
- c = nc = n c=nの場合、答えはシーケンスの合計から最小値を引いたものです。
- 1 <c <n 1 <c <n 1<c<N且N <2×CN <2 \倍のCn<2××cでは、この時点でシーケンスは1つのセグメントにしか分割できず、他のシーケンスは長さ1の間隔と見なすことができます。
- 1 <c <n 1 <c <n 1<c<N且2×C≤N 2 \回C \当量のn2××c≤nで、シーケンスを複数のセグメントに分割できますが、他のシーケンスは長さ1の間隔に分割できます。
したがって、間隔は2つだけです。長さ1と長さccです。c。
次に、主に1 <c <n 1 <c <nを検討します。1<c<nの状況。
この時点で、問題はdp問題になっていることがわかりました。
设 f i f_i f私aia_iを意味しますA私 締め切りでの答え、そして:
- 1≤i<c1 \ leq i <c 1≤私<c時間: fi = ∑ j = 1 iaj f_i = \ sum_ {j = 1} ^ {i} a_jf私=j = 1∑私AJ
- c≤i≤nc\ leq i \ leq n c≤私≤n時間: fi =min(fi − c + sumi − sumi − c − minni、fi − 1 + ai)f_i = \ min {(f_ {i --c} + sum_i --sum_ {i --c} --minn_i、 f_ {i-1} + a_i)}f私=分(fI - C+s u m私−s u mI - C−m i n n私、fI - 1+A私)
説明:合計s u mは接頭辞であり、配列minniminn_iです。m i n n私 表示 [ i − c + 1 , i ] [i - c + 1,i] [私は−c+1 、i ]この間隔の最小値。
待機:一定間隔の最小値?これは単調なキューが行うことではありません!
したがって、単調キューO(n)O(n)O(n) 解决 m i n n i minn_i m i n n私 ,然后 O ( n ) O(n) O (n ) dpは問題ありません。
全体の複雑さO(n)O(n)O (n )。
コード:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 10;
int n, c, q[MAXN], l = 1, r = 0;
LL a[MAXN], f[MAXN], minn[MAXN], sum[MAXN];
LL read()
{
LL sum = 0, fh = 1; char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') fh = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {
sum = (sum << 3) + (sum << 1) + (ch ^ 48); ch = getchar();}
return sum * fh;
}
int main()
{
n = read(), c = read();
for (int i = 1; i <= n; ++i) a[i] = read();
for (int i = 1; i <= n; ++i)
{
while (l <= r && q[l] + c <= i) l++;
while (l <= r && a[i] <= a[q[r]]) r--;
q[++r] = i; minn[i] = a[q[l]]; sum[i] = sum[i - 1] + a[i];
}
for (int i = 1; i < c; ++i) f[i] = f[i - 1] + a[i];
for (int i = c; i <= n; ++i) f[i] = min(f[i - c] + sum[i] - sum[i - c] - minn[i], f[i - 1] + a[i]);
printf("%lld\n", f[n]);
return 0;
}