一名苦逼的OIer,想成为ACMer

Iowa_Battleship

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编辑  收藏  举报

导航