zoukankan      html  css  js  c++  java
  • USACO 2009 Open 干草塔Tower of Hay 解题报告

    Description

    为了调整电灯亮度,贝西要用干草包堆出一座塔,然后爬到牛棚顶去把灯泡换掉。干草 包会从传送带上运来,共会出现N包干草,第i包干草的宽度是Wi,高度和长度统一为1。干 草塔要从底层开始铺建。贝西会选择最先送来的若干包干草,堆在地上作为第一层,然后再 把紧接着送来的几包干草包放在第二层,再铺建第三层……重复这个过程,一直到所有的干 草全部用完。每层的干草包必须紧靠在一起,不出现缝隙,而且为了建筑稳定,上层干草的 宽度不能超过下层的宽度。按顺序运来的干草包一定要都用上,不能将其中几个干草包弃置 不用。贝西的目标是建一座最高的塔,请你来帮助她完成这个任务吧。

    Input Format

    第一行:单个整数:N,1 ≤ N ≤ 100000 第二行到N + 1行:第i + 1行有一个整数Wi,1 ≤ Wi ≤ 10000

    Output Format

    第一行:单个整数,表示可以建立的最高高度

    Sample Input

    3 
    1 
    2 
    3

    Sample Output

    2

    Hint

    将1 和2放在第一层,将3放在第二层

    题解

    这题首先应该会想到贪心,但马上否定掉。但此时如果你完全放弃了贪心,那么你就很可能想不到正解了。

    由于此题阶段明显,可以断定用动规。在不考虑数据范围的前提下应该是很好做的。但N ≤ 100000,只能寻求更好的算法。

    再回想到贪心,这时会想到一个奇葩的感觉貌似是对的,又不知如何证明的推论:

    将塔看作一个面积一定图形,要使其最高,必须最瘦。

    事实上这个是正确的。证明引自张昆玮大牛

    任意取出一个能使层数最高的方案,设有CA层,把其中从下往上每一层最大的块编号记为Ai;任取一个能使底边最短的方案,设有CB层,把其中从下往上每一层最大的块编号记为Bi。显然A1>=B1,ACB<=BCB,这说明至少存在一个k属于(1,CB),满足Ak-1>=Bk-1且Ak<=Bk。也就是说,方案 A 第K 层完全被方案 B 第K 层包含。构造一个新方案,第K 层往上按方案 A,往下按方案 B,两边都不要的块放中间当第K 层。新方案的层数与 A 相同,而底边长度与 B 相同。证毕。

    (什么,看不懂?慢慢看吧。。耐心点就看懂了,想当初我也是如此熬过来的)

    于是底边最短的方案,层数最高。枚举最底层由哪几块组成,由此可以得出一个方程:从n到i中最底层的宽度f[i]=min(sum[j-1...i]) 满足 f[j]<=sum[j-1...i](因为这样会使得上一层的宽度不大于下一层的宽度) 且 n>=j>i

    其中i=n->1

    分析一下,发现f[i]=min(sum[j-1])-sum[i-1](令sum[i]表示宽度的前缀和),而sum[i]是随i的增大而增大的,所以从i到n一旦发现一个符合条件的决策j,便将其取出来更新f[i]。但是这仍不能通过所有数据。

    再次分析,发现所有的决策的值(例如对于决策j值即为sum[j-1])由n往前都是单调递减的,也就是一个比一个优。因此决定性的因素则是他们的生效时间。

    对于决策j:观察不等式f[j]<=sum[j-1...i]即f[j]<=sum[j-1]-sum[i-1],变形可得sum[i-1]<=sum[j-1]-f[j],发现sum[j-1]-f[j]便是其生效时间,也就是说在第一个i使得sum[i-1]<=sum[j-1]-f[j]之后到1决策j都是有效的。于是我们可以开一个单调队列,使得生效时间递减,并且队头始终生效并且队头+1未生效。由于后入队的决策比前面的优,于是一旦队头+1生效,队头便可弹掉。入队的时候,如果发现队尾-1的生效时间<队尾的生效时间,则弹掉队尾-1。

    如此一来便完美的解决了此题,复杂度为O(n)

    CODE:

    #include <cstdio>
    const int N=100000+9;
    int q[N],g[N],f[N],sum[N],w[N],n;
    int main()
    {
        scanf("%d",&n);
        for (int i=1;i<=n;++i) {
            scanf("%d",w+i);
            sum[i]=sum[i-1]+w[i];
        }
        q[1]=n+1;
        int h=1,t=1;
        for (int i=n;i;--i) {
            while (h<t && f[q[h+1]]<=sum[q[h+1]-1]-sum[i-1]) ++h;
            f[i]=sum[q[h]-1]-sum[i-1];
            g[i]=g[q[h]]+1; 
            q[++t]=i;
            while ((t>h) && (f[q[t-1]]-sum[q[t-1]-1]+sum[q[t]-1]>f[q[t]]))
                --t,q[t]=q[t+1];
        }
        printf("%d\n",g[1]);
    }
  • 相关阅读:
    Solution: Win 10 和 Ubuntu 16.04 LTS双系统, Win 10 不能从grub启动
    在Ubuntu上如何往fcitx里添加输入法
    LaTeX 笔记---Q&A
    Hong Kong Regional Online Preliminary 2016 C. Classrooms
    Codeforces 711E ZS and The Birthday Paradox
    poj 2342 anniversary party
    poj 1088 滑雪
    poj 2479 maximum sum
    poj 2481 cows
    poj 2352 stars
  • 原文地址:https://www.cnblogs.com/lazycal/p/2759427.html
Copyright © 2011-2022 走看看