题目来源
反思
因为目前在学习单调队列优化DP,所以会往单调队列上面想。然后犯了一个错误就是,认为这个题目只要用单调队列就可以完成,单调队列只是用来减少时间复杂度的,遇到了求最优解的题目,最先要想到的还是贪心和DP。
题解
- 分析题目
n 个烽火台,连续m个烽火台至少要有一个发出信号,每个烽火台具有代价,求最小的总代价。
求最值,想到DP、贪心。DP可以往前一个状态想,似乎可以由前一个状态转移过来,尝试使用DP去AC这题。
- DP分析
状态表示:
f
(
i
)
f(i)
f(i)表示选第
i
i
i个物品所有情况代价。
属性:
m
i
n
min
min
状态划分:要往前一个方向上想,那么就是:从前m个数中选,一个代价最小的数,加上当前代价。
方程为:
f
(
i
)
=
m
i
n
(
f
(
j
)
)
+
w
(
i
)
f(i) = min(f(j)) + w(i)
f(i)=min(f(j))+w(i) _____
j
∈
[
i
−
m
+
1
,
i
]
j \in [i - m + 1, i]
j∈[i−m+1,i]
那么
f
(
j
)
f(j)
f(j)就可以使用单调队列去优化时间复杂度。
单调队列的性质为单调递增,我们想要的值在队首。
接下就看代码即可。1
AC 代码
#include<bits/stdc++.h>
using namespace std;
#define _for(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rep(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl
#define mod(x) (x) % MOD
#define ENDL "\n"
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int N = 2e5 + 7, MOD = 1e9, INF = 0x3f3f3f3f;
int q[N], w[N], f[N];
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cout.tie(0), cin.tie(0);
int n, m;
cin >> n >> m;
_for(i, 1, n) cin >> w[i];
int hh = 0, tt = 0, ans = INF;
_for(i, 1, n) {
if (hh <= tt && i > q[hh] + m) ++hh;
f[i] = f[q[hh]] + w[i];
while (hh <= tt && f[i] <= f[q[tt]]) --tt;
q[++tt] = i;
if (i > n - m) ans = min(ans, f[i]);
}
cout << ans << ENDL;
return 0;
}
代码中有一个细节,就是进入循环的时候队列并不为空,而是入队了一个0进入,目的就是为了初始化 f f f的前m个元素,防止多加一次代价。 ↩︎