[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;
}