【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; }