• tyvj1305 最大子序和 【单调队列优化dp】


    描述

    输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大。

    例如 1,-3,5,1,-2,3

    当m=4时,S=5+1-2+3=7
    当m=2或m=3时,S=5+1=6

    输入格式

    第一行两个数n,m
    第二行有n个数,要求在n个数找到最大子序和

    输出格式

    一个数,数出他们的最大子序和

    测试样例1

    输入

    6 4 
    1 -3 5 1 -2 3

    输出

    7

    备注

    数据范围:
    100%满足n,m<=300000

    题解

    我们由题设f[i]为i位置最大子段和,得到状态转移方程f[i] = max(f[i - 1],sum[i] - sum[k]);  【i - k <= m】
    很明显这样做是O(n ^ 2)
    对于求sum[i] - sum[k]的最大值,我们可以用单调队列优化

    单调队列
    单调队列,顾名思义,就是单调的队列,用以O(1)求最值
    单调队列用双向队列维护,队首是最值【假设是最大】
    每次我们向队尾插入一个元素时,我们若队尾的元素比它要小就将他删除,直至队列为空或者队尾元素大于插入
    元素,再将其插入
    例如5 3 1,我们要插入4
    检查1 < 4,队列变为5 3
    检查3 < 4,队列变为5
    检查5 > 4,队列变为5 4
    插入完成

    你会发现这样的操作能满足队列一定单调,而队首就是我们要的值
    但注意随着时间的推移,队首元素可能“过时”,就是超出了我们所规定的范围,这个时候就删除队首,直至满足范围
    由于每个元素最多进队出队一次,所以总复杂度O(n)

    那么这题就好做了,我们用一个单调队列维护前m个sum值,每次只用O(1)就可以转移方程
    复杂度O(n)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define fo(i,x,y) for (int i = (x); i <= (y); i++)
    #define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next)
    using namespace std;
    const int maxn = 300005,maxm = 100005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1;char c = getchar();
    	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}
    	return out * flag;
    }
    int n,m,q[maxn],head,tail,sum[maxn],f[maxn];
    int main()
    {
    	n = read(); m = read();
    	REP(i,n) sum[i] = sum[i - 1] + read();
    	head = tail = 0; q[head] = 0;
    	for (int i = 1; i <= n; i++){
    		while (i - q[head] > m) head++;
    		f[i] = max(f[i - 1],sum[i] - sum[q[head]]);
    		q[++tail] = i;
    		while (tail > head && sum[q[tail]] < sum[q[tail - 1]]) q[tail - 1] = q[tail],tail--;
    	}
    	cout<<f[n]<<endl;
    	return 0;
    }
    


  • 相关阅读:
    移动端 细节点
    基于新版 node 的 vue 脚手架搭建
    全屏展示
    Vue 小实例
    移动端 模拟键盘 盖住表单
    decodeURI decodeURIComponent
    简单时钟
    全选 反选 传统写法
    星级点评 面向过程的传统写法
    JQ字符串截取
  • 原文地址:https://www.cnblogs.com/Mychael/p/8282827.html
Copyright © 2020-2023  润新知