【题目描述】
烽火台又称烽燧,是重要的防御设施,一般建在险要处或交通要道上。一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情。
在某两座城市之间有n个烽火台,每个烽火台发出信号都有一定的代价。 为了使情报准确的传递,在m个烽火台中至少要有一个发出信号。
现输入n、m和每个烽火台发出的信号的代价,请计算总共最少需要花费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确的传递。
【题目链接】
Tyvj 1313 烽火传递 【NOIP2010】
【解题思路】
动态规划,设$f[i]$表示在$[0, i]$中传递情报,且$i$烽火台必须参与传递的最小代价。
转移: $$ f[i] = \cases { w_i & 0 ≤ i < m \\ w_i + min\{\ f[k], k \in [i - m, i) \ \} & m ≤ i < n } $$ 即考虑前一个烽火台的位置。
上式在计算$f[i]$时需要区间最小值,且决策区间是向右平移的 。
因此我们使用单调队列来优化。
考虑到要保证情报的传递,答案为: $$ ans = max\{\ f[i]\ \}\ i \in [max(n - m, 0), n) $$
【AC代码】
#include <cstdio>
#include <climits>
#include <deque>
#include <algorithm>
#define MAXN 1000000
template <typename Item> struct MonoQueue{
std::deque<Item> data, aux;
void push(const Item &x){
data.push_back(x);
while(!aux.empty() && aux.back() > x) aux.pop_back();
aux.push_back(x);
}
void pop(){
Item x = data.front();
data.pop_front();
if(x == aux.front()) aux.pop_front();
}
size_t size(){
return data.size();
}
Item min(){
return aux.front();
}
};
int a[MAXN], f[MAXN];
int main(){
int n, m;
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++) scanf("%d", a + i);
MonoQueue<int> Q;
for(int i = 0; i < m; i++) Q.push(f[i] = a[i]);
for(int i = m; i < n; i++){
if(Q.size() == m + 1) Q.pop();
f[i] = Q.min() + a[i];
Q.push(f[i]);
}
int ans = INT_MAX;
for(int i = std::max(n - m, 0); i < n; i++) ans = std::min(ans, f[i]);
printf("%d\n", ans);
return 0;
}
就是这样啦。