NOIP2010「烽火传递」

题目

题目描述
  烽火台又称烽燧,是重要的军事防御设施,一般建在险要或交通要道上。一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息;夜晚燃烧干柴,以火光传递军情,在某两座城市之间有nn个烽火台,每个烽火台发出信号都有一定代价。为了使情报准确地传递,在连续mm个烽火台中至少要有一个发出信号。请计算总共最少花费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确传递。

输入
  第一行:两个整数NMN,M。其中N表示烽火台的个数,MM表示在连续mm个烽火台中至少要有一个发出信号。接下来NN行,每行一个数WiWi,表示第i个烽火台发出信号所需代价。

输出
  一行,表示答案。

样例输入

5 3
1
2
5
6
2

样例输出

4

数据范围限制
对于5050%的数据,MN1,000M≤N≤1,000
对于100100%的数据,MN100,000M≤N≤100,000Wi100Wi≤100

解题思路

看到这样一道题目,本人是第一时间想到了DP(挺正常的罢。。。
当然线段树也可以……但是DP简单又快呐!便用DP罢

很容易得出状态转移方程:
dpi={Wi(i≤M)min(dpj)+Wi(iMji1)(i>M)dp_i=\begin{cases} W_i& \text {(i≤M)} \\min(dp_j)+W_i(i-M≤j≤i-1)& \text{(i>M)} \end{cases}

如此以来,时间复杂度大约为O(NM)O(NM) ,(分数是60~70(亲测))

通过观察,我们发现每一次转移状态都会做min(dpj)min(dp_j)这个重复的操作,于是乎选用单调队列,将dpjdp_j丢入一个单调队列即可

AC代码:

Var n,m,i,head,tail:longint;
    w,dp,q:array[0..100005] of longint;
Begin
        read(n,m);
        for i:=1 to n do
        Begin
                read(w[i]);
        end;

        head:=1;
        tail:=1;
        for i:=1 to n do
        Begin
                dp[i]:=dp[q[head]]+w[i];

                while (tail>=head) and (dp[i]<=dp[q[tail]]) do dec(tail);
                tail:=tail+1;
                q[tail]:=i;

                while q[head]<i+1-m do inc(head);
        end;

        write(dp[q[head]]);
end.
posted @ 2019-10-11 20:41  willbe233  阅读(129)  评论(0)    收藏  举报