烽火传递

[NOIP2010初赛]烽火传递

◦有n个数,选择其中若干数,使得每连续m个数中都至少有一个数被选中,

且选出的数的和最小。(数的大小小于等于1000(不会爆int))

◦ m<=n<=1000。 (弱化版)

◦ m<=n<=100000。

单调队列优化的dp板子题。

先考虑弱化版怎么做:设dp[i]表示前i个数满足题意且第i个数被选出的情况下,选出的数的和的最小值。

所以dp[i]=min(dp[j])+a[i] (i-j+1<=m+1)

时间复杂度:O(n^2)

观察dp方程,我们发现这是一个滑动窗口的问题,故可以用单调队列优化解决。

时间复杂度降为O(n)

#include <iostream>
#include <cstdio>
using namespace std;
#define Maxn 200100
int n,m;
int a[Maxn],dp[Maxn];
int q[Maxn];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    int l=0,r=0;
    for(int i=1;i<=n;i++)
    {
        while(l<=r&&q[l]<i+1-m-1){l++;}
        dp[i]=dp[q[l]]+a[i];
        while(l<=r&&dp[i]<=dp[q[r]]){r--;}q[++r]=i;
    }
    int ans=2e9;
    for(int i=n;i>=max(n-m+1,1);i--)ans=min(ans,dp[i]);
    printf("%d",ans);
    return 0;
}
posted @ 2019-07-25 16:30  Akaina  阅读(124)  评论(0编辑  收藏  举报