NOIP2010【烽火传递】解题报告

题目

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

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

输出
  一行,表示答案。

样例输入

5 3
1
2
5
6
2

样例输出

4

数据范围限制
对于 50 50% 的数据, M N 1 , 000 M≤N≤1,000
对于 100 100% 的数据, M N 100 , 000 M≤N≤100,000 W i 100 Wi≤100

解题思路

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

很容易得出状态转移方程:
d p i = { W i (i≤M) m i n ( d p j ) + W i ( i M j i 1 ) (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 ( N M ) O(NM) ,(分数是60~70(亲测))

通过观察,我们发现每一次转移状态都会做 m i n ( d p j ) min(dp_j) 这个重复的操作,于是乎选用单调队列,将 d p j dp_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.
发布了23 篇原创文章 · 获赞 37 · 访问量 9052

猜你喜欢

转载自blog.csdn.net/weixin_41221124/article/details/102509468