UVA 12170 Easy Climb

本文最后更新于:2025年5月26日 下午

题目链接

很容易想到一个状态表示方法\(f[i][j]\)表示前\(i\)个点,最后一个高度为\(j\)时的最小花费。转移只用考虑上一个点填什么。
但是状态是表示不下的...

如果只有三个点,因为最左和最右不能改,那么中间的调整范围是\([\max (h-1-b,h-3-b),\min (h-1+b,h-3+b)]\),因为要代价最小,显然只能不调整或者取端点的高度。

yy 一下,发现其实每个点情况都类似,即\(h\)最终一定是\(p-k+qd\)这种形式。
\(1\leq p\leq n\), \(-n<q<n\)

所以把能用到的答案记录下来,第二维就被精简到了\(\mathjaxcal{O}(2n^2)\)

于是现在有了一个\(\mathjaxcal{O}(n^5)\)的做法。

\(h\)的大小顺序转移,因为每个点的合法区间都是递增的,所以可以用单调队列像滑动窗口那样优化。决策是有单调性的,虽然并不太会证
一个窗口内如果某时刻\(f[i-1][j]\)增大了,后面的值不会更小,自己画个图看看应该不难理解...
判断一下不合法的条件是\(|h-1-h-n|>(n-1)d\)
时间复杂度\(\mathjaxcal{O}(n^3)\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 105;
const ll INF = 1e18;
int n, T, tn, q[N], head;
ll a[N * N * 2], d, f[2][N * N * 2], h[N];

void init() {
tn = 0;
for (int i = 1; i <= n; i++)
for (int j = -n + 1; j <= n - 1; j++) {
a[++tn] = h[i] + j * 1ll * d;
}
sort(a + 1, a + tn + 1);
tn = unique(a + 1, a + tn + 1) - a - 1;
}
void solve() {
if (abs(h[n] - h[1]) > (n - 1) * 1ll * d) {puts("impossible"); return;}
init();
int now = 0;
for (int i = 1; i <= tn; i++) {
f[now][i] = INF;
if (a[i] == h[1]) f[now][i] = 0;
}
for (int i = 2; i <= n; i++) {
now ^= 1;
head = 1;
for (int j = 1; j <= tn; j++) {
while (head < tn && a[head] < a[j] - d) head++;
while (head < tn && f[now ^ 1][head + 1] <= f[now ^ 1][head] && a[head + 1] <= a[j] + d) head++;
if (f[now ^ 1][head] == INF) f[now][j] = INF;
else f[now][j] = abs(a[j] - h[i]) + f[now ^ 1][head];
}
}
for (int i = 1; i <= tn; i++) if (a[i] == h[n]) printf("%lld\n", f[now][i]);
}

int main() {
scanf("%d", &T);
while (T--) {
scanf("%d %lld", &n, &d);
for (int i = 1; i <= n; i++) scanf("%lld", &h[i]);
solve();
}
}

UVA 12170 Easy Climb
https://widsnoy.top/posts/2c8/
作者
widsnoy
发布于
2020年9月7日
许可协议