P2034选择数字题解

题目链接:在这里~

题目描述:

给定一行n个非负整数a[1]..a[n]。现在你可以选择其中若干个数,
但不能有超过k个连续的数字被选择。你的任务是使得选出的数字的和最大。

输入格式:

第一行两个整数n,k

以下n行,每行一个整数表示a[i]。

输出格式:

输出一个值表示答案。

思路:考虑反面,不能有超过k个连续的数字被选,等价于连续的k+1个数字中必须删一个,考虑如何去删数字,f[i]表示删了第i个数字的最小代价,f[i]可由(i-k-1)--->(i-1)转移过来,即在i前面的k+1个数字间,必须删一个,且 当前决策对后面无影响。n=1e5,O(n^2)的算法是过不去的,所以可以单调队列优化。

代码:

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<algorithm>
#define R register
#define ll long long int 
using namespace std;
const int N=1e5+5;
ll n,k,a[N],f[N],Min=1e16,fir=1,x[N],ans,head=1,tail=0;
int main(){
    scanf("%lld%lld",&n,&k);
    for(R int i=1;i<=n;++i){
    scanf("%lld",&a[i]);
    ans+=a[i];f[i]=Min;
    }
    f[0]=0;//第i个被删的最小代价
    for(R int i=1;i<=n;++i){
        if(i<=k+1)
        f[i]=a[i];
        else
        f[i]=f[x[head]]+a[i];
        while(head<=tail&&x[head]<i-k)head++;
        while(head<=tail&&f[x[tail]]>f[i])tail--;
        x[++tail]=i;
    }
    for(R int i=n;i>=n-k;--i)
    Min=min(Min,f[i]);
    printf("%lld",ans-Min);
    return 0;
}
原文地址:https://www.cnblogs.com/sky-zxz/p/9785130.html