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

选择数字

题目描述:
给定一行n个非负整数a[1]..a[n]。现在你可以选择其中若干个数,但不能有超过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[n];
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值