BZOJ1233 [Usaco2009Open]干草堆tower
一道单调队列优化\(DP\)
原题链接
首先想到的应该是\(O(n^3)\)的朴素\(DP\)。
定义\(f[i][j]\)表示第\(j\sim i\)块干草堆作为顶层时的最大高度。
\(\qquad\qquad f[i][j]=\max\limits_{k=1}^{j-1}\{f[j-1][k]\}+1,(sum[i]-sum[j-1]\leqslant sum[j-1]-sum[k-1])\)
但显然时间空间全炸了,这时我们需要知道一个结论,即最优解的最底层在所有解中一定是最小的(下面越窄才能叠的越高)。
任意取出一个能使层数最高的方案,设有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 相同。证毕。
证明by zkw
因为正着推并不单调,所以我们要倒着来推。
定义\(f[i]\)表示第\(i\sim n\)块干草堆构成的塔的最底层的宽度,\(h[i]\)表示状态\(f[i]\)下的最大高度。
\(\qquad\qquad f[i]=\min\limits_{j=i+1}^n\{sum[j-1]-sum[i-1]\},(f[j]\leqslant sum[j-1]-sum[i-1])\)
\(\qquad\qquad h[i]=h[j]+1\)
时间复杂度\(O(n^2)\)
显然\(j\)越小越好。
将\(f[i]\)的转移方程条件移项得\(sum[i-1]\leqslant sum[j-1]-f[j]\),所以对于一个决策\(k\),如果有一个决策\(j\)满足\(k>j\text{且}sum[k-1]-f[k]\leqslant sum[j-1]-f[j]\),即可认为\(k\)是无用决策。
这样就可以用单调队列来维护了。
#include<cstdio>
using namespace std;
const int N = 1e5 + 10;
int f[N], h[N], a[N], q[N];
int re()
{
int x = 0;
char c = getchar();
bool p = 0;
for (; c<'0' || c>'9'; c = getchar())
p = (c == '-' || p) ? 1 : 0;
for (; c >= '0'&&c <= '9'; c = getchar())
x = x * 10 + (c - '0');
return p ? -x : x;
}
int main()
{
int i, l = 1, r = 1, n;
n = re();
for (i = 1; i <= n; i++)
a[i] = a[i - 1] + re();
for (i = n, q[1] = n + 1; i; i--)
{
while (l < r && f[q[l + 1]] <= a[q[l + 1] - 1] - a[i - 1])
l++;
f[i] = a[q[l] - 1] - a[i - 1];
h[i] = h[q[l]] + 1;
while (l <= r && a[q[r] - 1] - f[q[r]] <= a[i - 1] - f[i])
r--;
q[++r] = i;
}
printf("%d", h[1]);
return 0;
}
posted on 2018-08-21 10:42 Iowa_Battleship 阅读(158) 评论(0) 编辑 收藏 举报