codevs3327选择数字(单调队列优化)

3327 选择数字

 

 时间限制: 1 s
 空间限制: 256000 KB
 题目等级 : 钻石 Diamond
 
 
题目描述 Description

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

 

输入描述 Input Description

第一行两个整数n,k

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

输出描述 Output Description

输出一个值表示答案。

样例输入 Sample Input

5 2

1

2

3

4

样例输出 Sample Output

12

数据范围及提示 Data Size & Hint

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

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

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

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

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

 

90分代码(其实数据弱,能A的,只是我太弱2333):

 

/* 
f[i]表示前i个数选k个的最大值
因为不能连续k个选,所以枚举一下断点 
但这个题貌似是个单调队列优化......
嗯,,,那就学吧
先上个暴力dp 
*/ 

#include<iostream>
#include<cstdio>
#define maxn 10000001

using namespace std;

int n,m,k;
int f[maxn],s[maxn],a[maxn];

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        s[i]=s[i-1]+a[i];
    }
    for(int i=1;i<k;i++)
      f[i]=s[i];
    for(int i=k;i<=n;i++)
      for(int j=i-k;j<=i;j++)
      {
          f[i]=max(f[i],f[j-1]+s[i]-s[j]);
      }
    printf("%d\n",f[n]);
} 
心若向阳,无谓悲伤

 

单调队列优化:

/*
 考虑从第一位开始递推处理
 在第i位的时候,需要在i-k位(取i-k+1到i)到i(不取i)中找一个点不取
 设这个点为j,这一段连续的就是j+1到i
 那么f[i] = max{f[j-1] + sum[j+1, i]} (i-k<=j<=i)
 运用前缀和简化一下就是f[i] = max{f[j-1]-sum[j]+sum[i]}+f[i];
 发现max里面的值只与j有关,所以可以用单调队列优化转移。
*/ 

#include<iostream>
#include<cstdio>
#define LL long long
#define maxn 100010

using namespace std;
LL n,k,a[maxn],s[maxn],f[maxn];
LL head,tail=1,d[maxn],q[maxn];

LL que(LL 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(LL i=1;i<=n;i++)
    scanf("%lld",&a[i]),s[i]=s[i-1]+a[i];
    for(LL i=1;i<=n;i++)
    {
        LL t=-10000000000;
        f[i]=que(i)+s[i];
    }
    cout<<f[n];
    return 0;
}
心若向阳,无言悲伤

 

 

posted @ 2017-01-28 18:24  安月冷  阅读(216)  评论(0编辑  收藏  举报