zoukankan      html  css  js  c++  java
  • 【优化】单调队列与dp

    笔者大概看了一下单调队列对于DP的优化,故撰此文,望有帮助。

    (dp还是推式子难啊qwq)

    例题1.

    题目大意:在n个数的序列中,选择数字,使得其连续不超过k个数,且和最大。

    本题的方程相对好推:设dp[i][0/1]为到了第i个数,且第i个数不选/选的最大值。

    则有转移:dp[i][0]=max(dp[i-1][0],dp[i-1][1])

    dp[i][1]=max{dp[j][0]-sum[j]+sum[i]},i-k<=j<i

    枚举j即可。

    但是题目会这么让你水过吗?

    发现会超时,优化不可避免。

    仔细观察方程,考虑它的特殊性。

    对于方程1,我们不便再多做什么。但是对于方程2,显然仍有优化余地。

    我们将sum[i]提出来,得到:

    dp[i][1]=max{dp[j][0]-sum[j]}+sum[i].

    这个方程只与j有关,我们让max里面的最大就好了。

    维护它,我们可以用堆,也可以用单调队列,线段树。

    本文主要讲对于单调队列的优化。它可以保证O(n)的时间复杂度。

    首先,我们明确一点,我们维护的队首元素最大。显然,队列中的数字要单调递减。

    其次,我们要严格确保我们查询的区间,保证队列中没有没用的数字。

    并且每次枚举到下一个数的时候,注意更新队列。

    考虑何时更新更优:

    首先,当这个数字不在需要的范围的时候,删除即可。

    其次,对于新插入的数字,我们要从队尾插入,比较哪个值更优:

    判断它们的“浪费情况”即可。

    用q[]表示队列,s[]表示前缀和,则判断:

    s[q[head]]-f[q[head]][0]>s[i]-f[i][0]&&head<=tail

    如果符合的话,就把它删掉吧。因为队尾的元素所浪费的比新插入的值多,显然一定不如它优。

    由此,我们已经保证了单调队列的稳定复杂度。

    给出代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    long long q[2000000],f[2000000][2];
    long long n,k,s[2000000],a[2000000];
    long long tail,head;
    int main(){
        scanf("%lld%lld",&n,&k);
        for(int i=1;i<=n;++i){
            scanf("%lld",&a[i]);
            s[i]=s[i-1]+a[i];//sum
        }
        tail=head=1;//初始化 
        for(int i=1;i<=n;++i){
            f[i][0]=max(f[i-1][0],f[i-1][1]);//对于不选i,只考虑前面两个即可 
            while(q[head]<i-k&&head<=tail)head++;//判断队头是否在所找区间内 
            f[i][1]=f[q[head]][0]-s[q[head]]+s[i];//取MAX转移 
            while(f[i][0]-s[i]>f[q[tail]][0]-s[q[tail]]&&head<=tail)tail--;
            q[++tail]=i;//更新队尾,当队列有数且当前队尾若插入i不满足单调性时
            //写成s[i]-f[i][0]<s[q[tail]]-f[q[tail]][0]也可以
            //可以理解为选到i和队尾时,两者不选的奶牛的效率和相比较,显然浪费少的更优,不优的删除即可 
        }printf("%lld
    ",max(f[n][0],f[n][1]));
        return 0;
    }

    双倍经验:P2034

    持续更新中。

  • 相关阅读:
    网络分析[3] 发布NAServer到ArcGIS for Server(以Server 10.4为例)
    Vuejs环境安装与工程建立【小白Windows向】
    【C语言学习】-05 二维数组、字符串数组、多维数组
    【C语言学习】-04 一维数组、字符数组
    【C语言学习】-03 循环结构
    【C语言学习】-02 分支结构
    【C语言学习】-01 C基础
    ios layoutsubView 何时被调用
    Xcode 断点调试,取消直接进入到汇编语言界面处理
    一步一步实现iOS应用PUSH功能
  • 原文地址:https://www.cnblogs.com/h-lka/p/11220841.html
Copyright © 2011-2022 走看看