备战Noip2018模拟赛10(B组) T1 Max 和最大

10月20日备战Noip2018模拟赛10

T1 Max和最大

题目描述

CYF的黑题,偏题,怪题,黑科技题,大码农题都做腻了,于是她想做一下签到水题,她希望从有一个长度为ň的整数序列(A1,A2,...,一个)中找出一段连续的长度不小于A,且不超过乙的子序列,使得这个子序列的和最大。

输入格式

第一行三个整数N,A,B(1 <= A <= B <= N)。第二行为Ñ整数,每个整数用空格隔开,表示该整数序列。

输出格式

一个整数,为CYF轻易求出的最大子序和。

输入样例

6 3 5
1 -3 5 1 -2 3

输出样例

7

样例解释

选从第三个数开始往后连续的四个数这四个数的和是5 + 1 +( - 2)+ 3 = 7

数据范围

对于30%的数据,N <= 1000

对于另外30%的数据,A = 1且B = n。

对于100%的数据,N <= 50万


思路

DP +单调队列

1.由求区间值,转移到求前缀和的差
.f [i]表示前i项的和,那么区间[i,j]的和即为f [i] -f [j-1],这样就简化了问题。

2.对于以i结尾的某个序列,j为前端点,则i-b + 1 <= j <= i-a + 1.
所以以i结尾的序列和的最优值为f [i] - F [J-1]

3.对转移过程进行优化,记录i-b + 1 <= j <= i-a + 1之间最小的值。这样就维护一个单调队列,把复杂度降为O(N)。
 

60分代码(不用单调队列)

#include <iostream>
#include <cstdio>
 
using namespace std;
 
const int maxn = 500005;
const int INF = -0x7fffffff;
 
int n, x[maxn], a, b, tmp, ans = INF, s[1001];
 
int main()
{
    scanf("%d%d%d", &n, &a, &b);
    for (int i = 1; i <= n; i ++){
        scanf("%d", &x[i]);
    }
    if (n <= 1000){
        for (int i = 1; i <= n; i ++){
			s[i] = s[i - 1] + x[i];
        }
        for (int i = 0; i <= n; i ++){
            for (int j = i + a; j <= min(i + b, n); j ++){
                ans = max(ans, s[j] - s[i]);
            }
        }
    }
    else{
        for (int i = 1; i <= n; i ++){
            if (x[i] + tmp < 0) tmp = 0;
            else tmp += x[i];
            if (tmp > ans) ans = tmp;
        }
    }
    printf("%d", ans);
    return 0;
}

代码

#include<iostream>
#include<cstdio>
#include<cstring>
 
using namespace std;
 
const int N = 500001;
const int INF = -0x7fffffff;
 
int ans = INF;
int i, j, k, n, m, a, b, t, w, p;
int d[N], f[N], s[N];
 
int main()
{
	freopen ("max.in", "r", stdin);
	freopen ("max.out", "w", stdout);
 
	cin >> n >> a >> b;
	for (i = 1; i <= n; i ++){
		cin >> k;
		f[i] = f[i - 1] + k;
		if(i >= a){
			w ++;
		    d[w] = f[i - a];
		    s[w] = i - a;
		    if (i - s[t] + 1 > b) t ++;
		    p = w - 1;
			for (j = p; j >= t; j --){
		    	if(d[w] < d[j]){
		    		d[j] = d[w];
		    		s[j] = s[w];
		    		w = j;
		    	}
				else break;
			}
			ans = max (ans, f[i] - d[t]);
		}
	}
	
	cout << ans;
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值