题目描述
烽火台是重要的军事防御设施,一般建在交通要道或险要处。
一旦有军情发生,则白天用浓烟,晚上有火光传递军情。
在某两个城市之间有nnn 座烽火台,每个烽火台发出信号都有一定的代价。
为了使情报准确传递,在连续 mmm 个烽火台中至少要有一个发出信号。
现在输入 n,mn,mn,m 和每个烽火台的代价,请计算在两城市之间准确传递情报所需花费的总代价最少为多少。
输入格式
第一行是两个整数 n,mn,mn,m,具体含义见题目描述;
第二行 nnn 个整数表示每个烽火台的代价 aia_iai。
输出格式
输出仅一个整数,表示最小代价。
数据范围
1≤n,m≤2×1051≤n,m≤2×10^51≤n,m≤2×105,
0≤ai≤10000≤a_i≤10000≤ai≤1000
输入样例
5 3
1 2 5 6 2
输出样例
4
算法思想(DP+单调队列优化)
在某两个城市之间有nnn 座烽火台,每个烽火台发出信号都有一定的代价,在连续 mmm 个烽火台中至少要有一个发出信号的情况下,求所需花费的最小总代价,典型的线性DP问题。
状态表示
f[i]
表示前i
座烽火台在满足条件的情况下,且点燃第i
座烽火台时所需花费的最小总代价。
状态计算
必须保证连续 mmm 个烽火台中至少要有一个发出信号,也就是说可以根据i
前面m - 1
座烽火台的状态计算出f[i]
,那么能转移到f[i]f[i]f[i]的合法状态的区间为[i−m,i−1][i - m, i - 1][i−m,i−1],因此:
f[i]=min{f[j]}+w[i]f[i]=\min\{f[j]\} + w[i]f[i]=min{f[j]}+w[i],i−m≤j<ii-m\le j<ii−m≤j<i。
其中min{f[j]}\min\{f[j]\}min{f[j]}就是求滑动窗口[i−m,i−1][i - m, i - 1][i−m,i−1]的最小值,可以使用单调队列进行优化,均摊时间复杂度为O(1)O(1)O(1)。
初始状态
f[0]=0f[0] = 0f[0]=0
时间复杂度
O(n)O(n)O(n)
代码实现
#include <iostream>
using namespace std;
const int N = 200010;
//f[i]表示前`i`座烽火台在满足条件的情况下,且点燃第i座烽火台时所需花费的最小总代价。
int f[N];
int w[N], q[N];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &w[i]);
f[0] = 0; //初始状态
int hh = 0, tt = 0;
q[tt] = 0; //初始状态入队
for(int i = 1; i <= n; i ++)
{
//从i前面的m - 1座烽火台取最值,所以窗口大小为m - 1
if(hh <= tt && q[hh] + m < i) hh ++;
//状态计算
f[i] = f[q[hh]] + w[i];
while(hh <= tt && f[q[tt]] >= f[i]) tt --;
q[++ tt] = i;
}
//打擂台求出最后一段区间中的最小值
int res = 1e9;
for(int i = n - m + 1; i <= n; i ++) res = min(res, f[i]);
cout << res << endl;
return 0;
}