zoukankan      html  css  js  c++  java
  • 循环数组最大子段和(带限制的最大子段和,单调队列优化)

    题目链接:here

     

     
    题解:对于不带限制的最大字段和我们可以:求一遍前缀和,求出最大值最小值,最后结果 res = max( MAX,  SUM - MIN );
    那么对于这道题相当于带了限制:限制最大子段的长度是len:我们可以维护一个前缀和 ,然后结果就是 max( sum[i] - min(sum[j]) ), i-len <= j <= i-1 && 1<=i <= 2*n,注意这里j>=i-len,而不是i-len+1,因为前缀和相减的时候要注意会把j位置上的那个数也减去)。然后这么看来 是n^2的dp,那么如何优化呢。
    可以预处理  长度为len的区间的最小值  然后对于每个i 查询前面区间长度为 len 的最小值 然后 sum[i] - sum[j]即可
    我们可以维护一个单调队列,单调队列要满足两点:
    • 队内元素的位置 要符合 (i)的区间要求 即 i-len <=que[j]<=i-1
    • 单调性,队列内的元素 尽可能小(因为我们要减去最小值呀),维护一个单调非递减队列(递增或持平)

    AC_Code:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int maxn = 1e6+10;
     5 const ll inf = 0x3f3f3f3f3f3f3f3f;
     6 deque<ll>q;
     7 ll a[maxn];
     8 ll n,m;
     9 ll sum[maxn];
    10 
    11 int main()
    12 {
    13     scanf("%lld",&n);
    14     for(ll i=1;i<=n;i++){
    15         scanf("%lld",&a[i]);
    16         sum[i] = sum[i-1]+a[i];
    17     }
    18     for(ll i=n+1;i<=2*n;i++){
    19         a[i] = a[i-n];
    20         sum[i] = sum[i-1]+a[i];
    21     }
    22     m = n;
    23     n<<=1;
    24 
    25     while( !q.empty() ) q.pop_front();
    26     ll res = 0;
    27     q.push_back(0);
    28     for(ll i=1;i<=n;i++){
    29         while( !q.empty() && q.front()<i-m ) q.pop_front();
    30         res = max(res,sum[i]-sum[q.front()]);
    31         while( !q.empty() && sum[q.back()]>=sum[i] ) q.pop_back();
    32         q.push_back(i);
    33     }
    34     printf("%lld
    ",res);
    35     return 0;
    36 }

    参考博客:here

  • 相关阅读:
    网络编程 并发-进程
    几个内置方法
    面向对象---两个内置函数和一些内置方法
    面向对象再进阶
    android设置图文提醒功能
    android 之实现手机震动功能
    android之实现选择对话框
    android 实现模拟加载中的程序
    android 简单对话框实现
    android 实现页面跳转及数据的传递和返回
  • 原文地址:https://www.cnblogs.com/wsy107316/p/13681095.html
Copyright © 2011-2022 走看看