C++题解:烽火传递——单调队列优化DP

博客围绕两城市间烽火台传递情报的最小代价问题展开。该问题是典型线性DP问题,需保证连续m个烽火台中至少一个发信号。通过状态表示、状态计算得出f[i]=min{f[j]}+w[i],可用单调队列优化,初始状态f[0]=0,时间复杂度O(n),还给出输入输出格式及样例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

烽火台是重要的军事防御设施,一般建在交通要道或险要处。

一旦有军情发生,则白天用浓烟,晚上有火光传递军情。

在某两个城市之间有nnn 座烽火台,每个烽火台发出信号都有一定的代价。

为了使情报准确传递,在连续 mmm 个烽火台中至少要有一个发出信号。

现在输入 n,mn,mn,m 和每个烽火台的代价,请计算在两城市之间准确传递情报所需花费的总代价最少为多少。

输入格式

第一行是两个整数 n,mn,mn,m,具体含义见题目描述;

第二行 nnn 个整数表示每个烽火台的代价 aia_iai

输出格式

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

数据范围

1≤n,m≤2×1051≤n,m≤2×10^51n,m2×105,
0≤ai≤10000≤a_i≤10000ai1000

输入样例

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][im,i1],因此:

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<iimj<i

其中min⁡{f[j]}\min\{f[j]\}min{f[j]}就是求滑动窗口[i−m,i−1][i - m, i - 1][im,i1]的最小值,可以使用单调队列进行优化,均摊时间复杂度为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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少儿编程乔老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值