【BZOJ1233】【usaco open 2009】干草堆 tower

【问题描述】

    Bessie必须建一座干草堆。一共有N大包的干草(1<=N<=100000)(从1到N编号)依靠传送带连续的传输进牛棚来。第i包干草有一个 宽度W_i(1<=w_i<=10000),高度为1。 Bessie必须利用所有N包干草来建立起干草堆,按顺序严格摆放。说得更清楚一些:一旦她将一个草包放在第二级 ,她不能将接下来的草包放在地基上。要求每一级不能比下面的一级宽。求最大高度。

【解析】

    这道题一眼看过去可能觉得可以贪心,但贪心是不行的,之后再说。

    正解还是动规。

    先要了解一个结论,在多种可行的堆叠方案中,至少有一种能使层数最高的方案同时使得底边最短。即底边最短的,层数一定最高。证明:(引用张昆玮大牛,虽然我不知道是谁)

     任意取出一个能使层数最高的方案,设有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 相同。证毕。

     知道这个结论后,贪心就明显不可行了。比如a + b> c+ d,b + c < d,贪心的结果是a,b,c||d,而a,b||c,d则更优。

     设F[i]为第i..N个干草包所叠成的塔底层最短边的值,同时用g[i]记下此时的层数。

     F[i]=min(sum[j-1]-sum[i-1])  j>i 且 sum[j-1]-sum[i-1]>=F[j]

     像这样O(n^2)的算法明显过不了,要进行优化。

     我们发现在阶段i时,对于k>j>i,j会比k优,决策为k的情况只能是J不满足条件而k满足条件,整理方程得

          f[j] - sum[j - 1] > f[k]-sum[k - 1]

     所以我们可以用一个单调队列来维护。均摊时间为o(n).

【代码】

     

//bzoj1233(lydsy)
//usaco 2009 open 


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
int sum[100001],f[100001],g[100001],q[100001];
int n,h,t;
using namespace std;
int main()
{
    freopen("tower.in","r",stdin);
    freopen("tower.out","w",stdout);
    cin >> n;
    sum[0] = 0;
    for (int i = 1;i <= n; i ++)
    {
        int j;
        cin >> j;
        sum[i] = sum[i - 1] + j;
    }
    
    h = t = 1; q[1] = n +1;
    for (int i = n; i >0; i --)
    {
        while (t > h && sum[q[h+1] - 1]-sum[i - 1] >= f[q[h +1]]) h++;
        f[i] = sum[q[h] - 1] - sum[ i - 1];
        g[i] = g[q[h]] +1;
        while (t > h && f[i] - sum[i- 1] < f[q[t]] - sum[q[t] - 1])
           --t;
        q[++t] = i;
    }
    
    cout << g[1];
    fclose(stdin);fclose(stdout);
    return 0;
}

 

      

posted @ 2012-07-11 09:00  N_C_Derek  阅读(1246)  评论(0编辑  收藏  举报