[Luogu] CF229D Towers

\(Link\)

Description

\(n(1\le{n}\le5000)\)座塔排在一条直线上,从左到右每个塔的高度分别为\(h_i(1\le{h_i}\le100000)\)

每次操作你可以选择一座塔(假设是第\(i\)座),用吊车把它吊起来,然后放到与它相邻的一座塔上(可以是第\(i-1\)座也可以是第\(i+1\)座),这样,新塔的高度为两座塔的和,完成操作后,塔的总数减少一座。

问最少需要多少次操作可以使得所有的塔从左到右形成一个非递减序列。

Solution

感觉和划分的64pts部分有点像?

我们设\(dp[i]\)表示以\(i\)为结尾的答案(最少操作次数),\(ml[i]\)表示\(i\)所属的塔可能的最小高度。设\(s_i\)\(h_i\)的前缀和,那么我们枚举\(j\sim{i}\)被合并,如果\(ml[j-1]\le{s[i]-s[j-1]}\)就可以相应的更新即可。

Code

#include <bits/stdc++.h>

using namespace std;

int n, h[5005], dp[5005], s[5005], ml[5005];

int read()
{
	int x = 0, fl = 1; char ch = getchar();
	while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
	return x * fl;
}

int main()
{
	n = read();
	for (int i = 1; i <= n; i ++ )
	{
		h[i] = read();
		s[i] = s[i - 1] + h[i];
	}
	memset(dp, 0x3f, sizeof(dp));
	memset(ml, 0x3f, sizeof(ml));
	dp[0] = ml[0] = 0; 
	for (int i = 1; i <= n; i ++ )
	{
		for (int j = 1; j <= i; j ++ )
		{
			if (ml[j - 1] <= s[i] - s[j - 1])
			{
				ml[i] = min(ml[i], s[i] - s[j - 1]);
				dp[i] = min(dp[i], dp[j - 1] + i - j);
			}
		}
	}
	printf("%d\n", dp[n]);
	return 0;
}
posted @ 2020-11-21 15:39  andysj  阅读(92)  评论(0编辑  收藏  举报