【单调队列优化DP】烽火传递 LibreOJ - 10180

题目来源

点我进入提交题目

反思

因为目前在学习单调队列优化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;
}

  1. 代码中有一个细节,就是进入循环的时候队列并不为空,而是入队了一个0进入,目的就是为了初始化 f f f的前m个元素,防止多加一次代价。 ↩︎

上一篇:LOJ#515. 「LibreOJ β Round #2」贪心只能过样例(bitset)


下一篇:LibreOJ #10046