• BZOJ1233 [Usaco2009Open]干草堆tower[贪心+单调队列优化]


    地址


    注意思路!多看几遍!

    很巧妙的一道题。不再是决策点以dp值中一部分含j项为维护对象,而是通过维护条件来获取决策。

    首先有个贪心策略,让底层的宽度尽可能小,才能让高度尽可能高。所以应该倒着dp,表示堆$i$~$n$的最高高度$f[i]$,同时这种最值应来源于之后的j,要在设一个$g[i]$表示以i为底层,最窄的宽度。这个的话真的只可意会啊。注意$g[i]$没人告诉你是单调的,$g[i]$之后一个不合法的决策都可能有$g[j]>g[i]$,所以单调性问题还当谨慎考虑。

    所以dp方程就能出来了

    $g[i]=min(sum[j-1]-sum[i-1]) $          $  g[j]<=sum[j-1]-sum[i-1]$

    $f[i]=f[j]+1$

    然后这个是$O(n^2)$的,考虑优化。从min内可以看出,j越小越好。那瓶颈就在于后面的那个约束条件怎么用,就是我在保证条件的情况下取j最小。

    转化条件:$g[j]<=sum[j-1]-sum[i-1]$移项得$sum[j-1]-g[j]≥sum[i-1]$,而$sum[i-1]$是单调减的,那我要之前的j得插入$sum[j-1]-g[j]$,在dp到i时去把决策中大于等于$sum[i-1]$的找一个最小j。

    于是单调队列维护j单调递减,$sum[j-1]-g[j]$单调减元素。假设dp完i后,我将目前i要向单调队列队尾比较。如果$sum[j-1]-g[j]>=sum[q[r]-1]-g[q[r]]$也就是j可以发挥与队尾同等的作用(甚至更优)的话,而我又渴求j尽可能小,那队尾就没什么卵用可以弹出了。直到不满足上述比较,就在队尾插入。这样保证$sum[j-1]-g[j]$单调减,我要取合法决策,便是队列头开始的一段大于等于$sum_i$的。注意,我要j最小,那队头满足条件的若干个决策,只要j最小的,那我可以依据j单调性将队头不断pop(反正之后$sum[i-1]$只会更小,现在留着也没用),直到最后一个满足条件的,就是最小的j。还是有点难以理解,贴个图。

     

    然后每次就可以取到最小满足条件j了。

    这个故事告诉我们单调队列的队头并不就一定是检查队头合法性的(范围之类的),也可以排除不优决策,这依赖于队头和队头下一个元素间的相互比较,要结合单调性予以考虑。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<cmath>
     6 using namespace std;
     7 typedef long long ll;
     8 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
     9 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
    10 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
    11 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
    12 template<typename T>inline T read(T&x){
    13     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
    14     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
    15 }
    16 const int N=100000+7;
    17 int sum[N],f[N],g[N],q[N];
    18 int n,l,r;
    19 
    20 int main(){//freopen("test.in","r",stdin);//freopen("tmp.out","w",stdout);
    21     read(n);for(register int i=1;i<=n;++i)sum[i]=read(sum[i])+sum[i-1];
    22     l=1,r=1;q[l]=n+1;
    23     for(register int i=n;i;--i){
    24         while(l<r&&sum[q[l+1]-1]-g[q[l+1]]>=sum[i-1])++l;
    25         g[i]=sum[q[l]-1]-sum[i-1],f[i]=f[q[l]]+1;
    26         while(l<=r&&sum[q[r]-1]-g[q[r]]<=sum[i-1]-g[i])--r;
    27         q[++r]=i;
    28     }
    29     printf("%d
    ",f[1]);
    30     return 0;
    31 }
  • 相关阅读:
    http://localhost:8080/ 演出Oracle说明
    JS浏览器类型推断方法
    MVC设计模式JavaWeb实现
    《TCP/IP详细说明》读书笔记(17章)-TCP传输控制协定
    创建表单
    道路软件质量:SourceMonitor
    HDU
    HDU 3032 Nim or not Nim? (sg函数求解)
    OpenWRT推理client线上的数
    IIS的ISAPI接口简介
  • 原文地址:https://www.cnblogs.com/saigyouji-yuyuko/p/10453611.html
Copyright © 2020-2023  润新知