题目链接:https://loj.ac/problem/10180
解题思路
我们设dp[i][0]表示以 i 为结尾不选 i 满足条件的最小代价。dp[i][1]表示以 i 为结尾选 i 满足条件的最小代价。那么状态转移方程如下。
dp[i][1] = min(dp[i-1][1],dp[i-1][0])+a[i]
dp[i][0] = min(dp[j][1]|0<i-j<m)
dp[i][1]直接转移,dp[i][0]可由其前m-1个中最小的dp[j][1]转移,这就可以利用单调队列维护。最终复杂度是O(n)的。
AC代码
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=2e5+5;
int n,m;
int a[N];
int dp[N][2];//dp[i][0]表示以i为结尾,不选i的最小代价,dp[i][1]表示以i为结尾,选i的最小代价
struct node
{
int val,pos;
}q[N];
int l,r;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
}
if(m==1)
{
int ans=0;
for(int i=1;i<=n;++i)
ans+=a[i];
printf("%d\n",ans);
return 0;
}
q[r].val=0;
q[r++].pos=0;
for(int i=1;i<=n;++i)
{
dp[i][1]=min(dp[i-1][1],dp[i-1][0])+a[i];
while(l<r&&(i-q[l].pos)>=m)
l++;
dp[i][0]=q[l].val;
while(l<r&&q[r-1].val>=dp[i][1])
r--;
q[r].val=dp[i][1];
q[r++].pos=i;
}
printf("%d\n",min(dp[n][0],dp[n][1]));
return 0;
}