您的位置:首页 > 其它

选择数字(动态规划+单调队列)

2016-11-11 09:45 288 查看

选择数字

题目描述:

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

输入描述:

第一行两个整数n,k

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

输出描述:

输出一个值表示答案。

样例输入:

5 2

1

2

3

4

5

样例输出:

12

数据范围及提示:

对于20%的数据,n <= 10

对于另外20%的数据, k = 1

对于60%的数据,n <= 1000

对于100%的数据,1 <= n <= 100000,1 <= k <= n,

0 <= 数字大小 <= 1,000,000,000

思路:

考虑动归,在第i点时,在i-k到i中肯定有一个点j不能选择,即:j为断点。

所以f[i]=max(f[i],f[j-1]+a[j+1]+a[j+2]……a[i])(i-k<=j<=i)

所以维护前缀和,然后方程就变成了

f[i]=max(f[i],f[j-1]+sum[i]-sum[j]) (i-k<=j<=i)

变形一下变成:f[i]=max(f[i],f[j-1]-sum[j])+sum[i] (i-k<=j<=i)

发现max里面的值只与j有关,所以可以用单调队列优化转移。

#include<iostream>
#include<cstdio>
#define lon long long
using namespace std;
const int maxn=100010;
lon n,k,a[maxn],s[maxn],f[maxn];
lon head,tail=1,d[maxn],q[maxn];
lon que(lon j)
{
d[j]=f[j-1]-s[j];
while(head<=tail&&d[q[tail]]<d[j]) tail--;
q[++tail]=j;
while(head<=tail&&q[head]<j-k) head++;
return d[q[head]];
}
int main()
{
scanf("%lld%lld",&n,&k);
for(lon i=1;i<=n;i++)
scanf("%lld",&a[i]),s[i]=s[i-1]+a[i];
for(lon i=1;i<=n;i++)
{
lon t=-10000000000;
/*for(lon j=i-k;j<=i;j++)
t=max(t,f[j-1]-s[j]);*/
f[i]=que(i)+s[i];
}
cout<<f
;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: