嘘~ 正在从服务器偷取页面 . . .

LOJ10180 「一本通 5.5 练习 1」烽火传递 题解


LOJ10180 「一本通 5.5 练习 1」烽火传递 题解

题目链接:「一本通 5.5 练习 1」烽火传递

题意

原题来自:NOIP 2010 提高组初赛 · 完善程序

烽火台是重要的军事防御设施,一般建在交通要道或险要处。一旦有军情发生,则白天用浓烟,晚上有火光传递军情。

在某两个城市之间有 \(n\) 座烽火台,每个烽火台发出信号都有一定的代价。为了使情报准确传递,在连续 \(m\) 个烽火台中至少要有一个发出信号。现在输入 \(n,m\) 和每个烽火台的代价,请计算总共最少的代价在两城市之间来准确传递情报。

输入格式

第一行是 \(n,m\),表示 \(n\) 个烽火台和连续烽火台数 \(m\)

第二行 \(n\) 个整数表示每个烽火台的代价 \(a_i\)

输出格式

输出仅一个整数,表示最小代价。

记得哪次模拟赛考过一题,在区间中选不相连的若干段且每段长度都不超过 \(m-1\) ,求最大的和。

这题实际上就是反过来的,而且还有两个城市需要连起来。

首先我们在序列左右各放一个花费为 \(0\) 的烽火台,然后设 \(f_i\)\(i\) 必选时的最小花费。

\(f\) 的初值为 \(+\infty\) ,除了 \(f_0=0\) 。转移很简单 \[ f_i = \min\left\{f_j\right\} + a_i\quad(i - m \le j < i) \] 这个显然可以用单调队列优化,最后答案就是 \(f_{n+1}\)

时间复杂度 \(\mathcal{O}(n)\)

代码:(我单调队列的写法是 (st,en] 的)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(2e5 + 15))

int n, m, st, en, S, a[N], q[N], dp[N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m; q[en = 1] = st = 0;
    memset(dp, 0x3f, sizeof(dp)); dp[0] = 0;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n + 1; i++)
    {
        while(st < en && i - q[st + 1] > m) ++st;
        dp[i] = dp[q[st + 1]] + a[i];
        while(st < en && dp[q[en]] > dp[i]) --en;
        q[++en] = i;
    }
    cout << dp[n + 1] << '\n';
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录