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_{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;
}