题目链接:在这里~
题目描述:
给定一行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; }