题目链接:传送门
设表示处理到第个位置的最小花费
很明显转移要从之前的个位置转移过来
就是
最后答案要取
这样转移是的,但足以通过本题的
正解是单调队列优化
由于只是求一个区间的最小值,所以线段树也可以,我下面写了
暴力:
#include <bits/stdc++.h>
#define
using namespace std;
int n, a[A], f[A], m, ans = A;
int main(int argc, char const *argv[]) {
cin >> n >> m; memset(f, 0x3f, sizeof f); f[1] = 0;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
for (int j = i - 1; j >= max(1, i - m); j--)
f[i] = min(f[i], f[j] + a[i]);
for (int i = n - m + 1; i <= n; i++) ans = min(ans, f[i]);
cout << ans << endl;
}
线段树优化,相比单调队列多了常数还大:
#include <bits/stdc++.h>
#define
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
int n, a[A], f[A], m, ans = A;
struct node {int l, r, w;}tree[A];
void build(int k, int l, int r) {
tree[k].l = l; tree[k].r = r; tree[k].w = inf;
if (l == r) return;
int m = (l + r) >> 1;
build(k << 1, l, m); build(k << 1 | 1, m + 1, r);
}
void change(int k, int pos, int val) {
if (tree[k].l == tree[k].r) {tree[k].w = val; return;}
int m = (tree[k].l + tree[k].r) >> 1;
if (pos <= m) change(k << 1, pos, val);
else change(k << 1 | 1, pos, val);
tree[k].w = min(tree[k << 1].w, tree[k << 1 | 1].w);
}
int ask(int k, int l, int r) {
if (tree[k].l >= l and tree[k].r <= r) return tree[k].w;
int m = (tree[k].l + tree[k].r) >> 1, ans = inf;
if (l <= m) ans = min(ans, ask(k << 1, l, r));
if (r > m) ans = min(ans, ask(k << 1 | 1, l, r));
return ans;
}
int main(int argc, char const *argv[]) {
cin >> n >> m; build(1, 1, n); change(1, 1, 0);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
change(1, i, min(ask(1, i, i), ask(1, max(1, i - m), max(1, i - 1)) + a[i]));
cout << ask(1, n - m + 1, n) << endl;
}