[UESTC 594] 我要长高

题目链接:

我要长高

题目分析:

\(dp[i][j]\)表示到第\(i\)个人,他的身高是\(j\)的时候的最小损失,然后得到一个朴素的转移方程
\(dp[i][j] = min(dp[i - 1][k] + abs(k - j) * C + (j - a[i]) ^ 2)\)
把无关的丢到\(min\)外面来

\[ dp[i][j] = \left\{ \begin{aligned} min(dp[i - 1][k] + k * C) - j * C + (j - a[i]) ^ 2 (j < k) \\ min(dp[i - 1][k] - k * C) + j * C + (j - a[i]) ^ 2 (j > k)\\ \end{aligned} \right. \]

直接枚举\(i,j,k\)显然是\(O(n^3)\)的,看到有\(min / max\)考虑怎么单调队列一下
然后发现好像不用单调队列也可以,我们只需要存储当前\(min\)里面那一坨的最小值就可以了

#include<bits/stdc++.h>
#define INF (1000000000 + 7)
#define N (50000 + 10) 
using namespace std;
inline int read() {
	int cnt = 0, f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
	while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + c - '0'; c = getchar();}
	return cnt * f;
}
int n, C, a[N], dp[N][105];
int gmin, lim, ans;
inline int Pow(int x) {return x * x;}
int main() {
//	freopen("1.in", "r", stdin);
	while (scanf("%d", &n) != EOF) {
		C = read();
		memset(dp, 0x3f3f, sizeof(dp));
		for (register int i = 1; i <= n; ++i) a[i] = read(), lim = max(lim, a[i]);
		for (register int i = a[1]; i <= lim; ++i) dp[1][i] = Pow(i - a[1]);
		for (register int i = 2; i <= n; ++i) {
			gmin = INF;
			for (register int j = a[i - 1]; j <= lim; ++j) {
				gmin = min(gmin, dp[i - 1][j] - C * j);
				if (j >= a[i]) dp[i][j] = gmin + Pow(j - a[i]) + C * j;
			}
			gmin = INF;
			for (register int j = lim; j >= a[i]; --j) {
				gmin = min(gmin, dp[i - 1][j] + C * j);
				if (j >= a[i]) dp[i][j] = min(dp[i][j], gmin + Pow(j - a[i]) - C * j);
			}
		}
		ans = INF;
		for (register int i = a[n]; i <= lim; ++i) ans = min(ans, dp[n][i]);
		printf("%d\n", ans);
	}
	return 0;
}
posted @ 2019-09-11 21:00  kma_093  阅读(183)  评论(0编辑  收藏  举报