POJ 3666. Making the Grade
Description
A straight dirt road connects two fields on FJ’s farm, but it changes elevation more than FJ would like. His cows do not mind climbing up or down a single slope, but they are not fond of an alternating succession of hills and valleys. FJ would like to add and remove dirt from the road so that it becomes one monotonic slope (either sloping up or down).
You are given N integers A 1 , . . . , A N ( 1 ≤ N ≤ 2 , 000 ) A^{}_{1}, ... , A^{}_{N} (1 \le N \le 2,000) A1,...,AN(1≤N≤2,000) describing the elevation ( 0 ≤ A i ≤ 1 , 000 , 000 , 000 ) (0 \le A^{}_{i} \le 1,000,000,000) (0≤Ai≤1,000,000,000) at each of N equally-spaced positions along the road, starting at the first field and ending at the other. FJ would like to adjust these elevations to a new sequence B 1 , . . . . , B N B^{}_{1}, . ... , B^{}_{N} B1,....,BN that is either nonincreasing or nondecreasing. Since it costs the same amount of money to add or remove dirt at any position along the road, the total cost of modifying the road is
∣ A 1 − B 1 ∣ + ∣ A 2 − B 2 ∣ + . . . + ∣ A N − B N ∣ |A^{}_{1} - B^{}_{1}| + |A^{}_{2} - B^{}_{2}| + ... + |A^{}_{N} - B^{}_{N} | ∣A1−B1∣+∣A2−B2∣+...+∣AN−BN∣
Please compute the minimum cost of grading his road so it becomes a continuous slope. FJ happily informs you that signed 32-bit integers can certainly be used to compute the answer.
Input
- Line 1: A single integer: N
- Lines $2…N+1: Line i+1 contains a single integer elevation: A i A^{}_{i} Ai
Output
- Line 1: A single integer that is the minimum cost for FJ to grade his dirt road so it becomes nonincreasing or nondecreasing in elevation.
Sample Input
7
1
3
2
4
5
3
9
Sample Output
3
\newline
\newline
\newline
思路
首先把这题的模型抽象出来,就是给定一个数组,你通过调整数组 A A A 的值使数组变成单调不减或者单调不增,求数组每个数字的变化绝对值之和最小是多少。记调整完以后的符合要求的数组为 A ′ A' A′。我们可以先考虑单调不减的情况,单调不增同理。
我们考虑两个相邻的
A
[
i
−
1
]
A[i - 1]
A[i−1] 和
A
[
i
]
A[i]
A[i],如果
A
[
i
−
1
]
A[i - 1]
A[i−1] >
A
[
i
]
A[i]
A[i],那么我们只需要提高
A
[
i
]
A[i]
A[i] 的值到
A
′
[
i
]
=
A
[
i
−
1
]
A'[i] = A[i - 1]
A′[i]=A[i−1] 就行,而不需要提高到
A
[
i
−
1
]
+
1
,
A
[
i
−
1
]
+
2....
A[i - 1] + 1, A[i - 1] + 2 ....
A[i−1]+1,A[i−1]+2....,因为提高到
A
[
i
−
1
]
A[i - 1]
A[i−1] 已经满足要求。同理我们也可以降低
A
[
i
−
1
]
A[i - 1]
A[i−1] 的值到
A
′
[
i
−
1
]
=
A
[
i
]
A'[i - 1] = A[i]
A′[i−1]=A[i]。这里有一个直觉,那就是:
\newline
\newline
猜想
最终的数组 A ′ A' A′ 的值都是属于原数组 A A A 的值的集合。
\newline
\newline
证明
我们把原数组连同 − ∞ -\infty −∞ 和 + ∞ +\infty +∞ 从小到大排序,得到新数组 a a a,有
a
0
≤
a
1
≤
.
.
.
.
.
.
≤
a
N
+
1
a^{}_{0} \le a^{}_{1}\le...... \le a^{}_{N +1}
a0≤a1≤......≤aN+1
其中
a
0
a^{}_{0}
a0 =
−
∞
-\infty
−∞,
a
N
+
1
a^{}_{N +1}
aN+1 =
+
∞
+\infty
+∞
\newline
\newline
记调整后的数组为 c c c,我们要证明相邻的 a i a^{}_{i} ai 和 a i + 1 a^{}_{i +1} ai+1 之间不存在任何数组 c c c 中的值。即不存在任何 c j c^{}_{j} cj … c k c^{}_{k} ck 使得
a i < c j ≤ c j + 1 ≤ . . . . ≤ c k < a i + 1 a^{}_{i} < c^{}_{j} \le c^{}_{j + 1} \le.... \le c^{}_{k} < a^{}_{i + 1} ai<cj≤cj+1≤....≤ck<ai+1
假设存在这样的 c j c^{}_{j} cj … c k c^{}_{k} ck,记 c t c^{}_{t} ct 对应的原数组中的值为 x , j ≤ t ≤ k x,j \le t \le k x,j≤t≤k。
如果 c t c^{}_{t} ct ≤ x \le x ≤x,那么有
a
i
<
c
j
≤
c
j
+
1
≤
.
.
.
.
≤
c
k
<
a
i
+
1
≤
x
a^{}_{i} < c^{}_{j} \le c^{}_{j + 1} \le.... \le c^{}_{k} < a^{}_{i + 1} \le x
ai<cj≤cj+1≤....≤ck<ai+1≤x
如果我们把
c
t
c^{}_{t}
ct 变到
a
i
+
1
a^{}_{i + 1}
ai+1,那么总的目标和会降低
a
i
+
1
a^{}_{i + 1}
ai+1 -
c
t
c^{}_{t}
ct,如果我们把
c
t
c^{}_{t}
ct 变到
a
i
a^{}_{i}
ai,总的目标和会提高
c
t
c^{}_{t}
ct -
a
i
a^{}_{i}
ai。
\newline
同理,如果有
c
t
c^{}_{t}
ct
≥
x
\ge x
≥x,则
x
≤
a
i
<
c
j
≤
c
j
+
1
≤
.
.
.
.
≤
c
k
<
a
i
+
1
x \le a^{}_{i} < c^{}_{j} \le c^{}_{j + 1} \le.... \le c^{}_{k} < a^{}_{i + 1}
x≤ai<cj≤cj+1≤....≤ck<ai+1
如果我们把
c
t
c^{}_{t}
ct 变到
a
i
+
1
a^{}_{i + 1}
ai+1,那么总的目标和会提高
a
i
+
1
a^{}_{i + 1}
ai+1 -
c
t
c^{}_{t}
ct,如果我们把
c
t
c^{}_{t}
ct 变到
a
i
a^{}_{i}
ai,总的目标和会降低
c
t
c^{}_{t}
ct -
a
i
a^{}_{i}
ai。
\newline
\newline
现在我们记
c
j
c^{}_{j}
cj …
c
k
c^{}_{k}
ck 当中有
m
1
m^{}_{1}
m1 个数小于对应的原数组中的数字,有
m
2
m^{}_{2}
m2 个数大于对应的原数组中的数字。
\newline
如果
m
1
≤
m
2
m^{}_{1} \le m^{}_{2}
m1≤m2,那么我们把
c
j
c^{}_{j}
cj …
c
k
c^{}_{k}
ck 中所有的数字降低
c
j
c^{}_{j}
cj -
a
i
a^{}_{i}
ai,那么新的数组
c
c
c 变成
a
i
=
c
j
′
≤
c
j
+
1
′
≤
.
.
.
.
≤
c
k
′
<
a
i
+
1
a^{}_{i} = c'^{}_{j} \le c'^{}_{j + 1} \le.... \le c'^{}_{k} < a^{}_{i + 1}
ai=cj′≤cj+1′≤....≤ck′<ai+1
新的目标和
S
′
=
S
+
m
1
∗
(
c
j
−
a
i
)
−
m
2
∗
(
c
j
−
a
i
)
≤
S
S' = S + m^{}_{1} * (c^{}_{j} - a^{}_{i}) - m^{}_{2} * (c^{}_{j} - a^{}_{i}) \le S
S′=S+m1∗(cj−ai)−m2∗(cj−ai)≤S
如果 m 1 ≥ m 2 m^{}_{1} \ge m^{}_{2} m1≥m2,那么我们把 c j c^{}_{j} cj … c k c^{}_{k} ck 中所有的数字提高 a i + 1 a^{}_{i +1} ai+1 - c k c^{}_{k} ck,那么新的数组 c c c 变成
a
i
<
c
j
′
≤
c
j
+
1
′
≤
.
.
.
.
≤
c
k
′
=
a
i
+
1
a^{}_{i} < c'^{}_{j} \le c'^{}_{j + 1} \le.... \le c'^{}_{k} = a^{}_{i + 1}
ai<cj′≤cj+1′≤....≤ck′=ai+1
新的目标和
S
′
=
S
−
m
1
∗
(
a
i
+
1
−
c
k
)
+
m
2
∗
(
a
i
+
1
−
c
k
)
≤
S
S' = S - m^{}_{1} * (a^{}_{i +1} - c^{}_{k}) + m^{}_{2} * (a^{}_{i + 1} - c^{}_{k}) \le S
S′=S−m1∗(ai+1−ck)+m2∗(ai+1−ck)≤S
\newline
\newline
综上,我们总可以通过调整
c
j
′
=
a
i
c'^{}_{j} = a^{}_{i}
cj′=ai 或者
c
k
′
=
a
i
+
1
c'^{}_{k} = a^{}_{i + 1}
ck′=ai+1,来使新的目标和
S
′
S'
S′ 不增,这样我们可以重复上述的步骤使得所有的
c
j
c^{}_{j}
cj …
c
k
c^{}_{k}
ck 都变成
a
i
a^{}_{i}
ai 或者
a
i
+
1
a^{}_{i + 1}
ai+1。故而猜想得证。
\newline
\newline
\newline
最初的动态规划想法
上面证明了调整后的新数组的值必然都处于原数组内,那么这个问题就有点像完全背包问题了。记原数组中的值的集合为 B B B, 每一个 A ′ [ i ] A'[i] A′[i] 都可以从 B B B 中取一个数字,而且同一个数字可以重复取。
我们定义状态
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 为
A
′
A'
A′中的第
i
i
i 个数字
A
′
[
i
]
=
B
[
j
]
A'[i] = B[j]
A′[i]=B[j] 时,最小的目标和。状态转移方程为:
d
p
[
i
]
[
j
]
=
m
i
n
B
[
k
]
≤
B
[
j
]
(
∣
A
[
i
]
−
B
[
j
]
∣
+
d
p
[
i
−
1
]
[
k
]
)
dp[i][j] = min_{B[k] \le B[j]} (|A[i] - B[j]| + dp[i - 1][k])
dp[i][j]=minB[k]≤B[j](∣A[i]−B[j]∣+dp[i−1][k])
我们可以自定义一个
B
B
B 数组的顺序,不妨把
B
B
B 从小到大排序,那么状态转移方程可以写成:
d
p
[
i
]
[
j
]
=
m
i
n
0
≤
k
≤
j
(
∣
A
[
i
]
−
B
[
j
]
∣
+
d
p
[
i
−
1
]
[
k
]
)
dp[i][j] = min_{0 \le k \le j} (|A[i] - B[j]| + dp[i - 1][k])
dp[i][j]=min0≤k≤j(∣A[i]−B[j]∣+dp[i−1][k])
这个算法时间复杂度为
O
(
n
3
)
O(n^{3})
O(n3),对于
n
≤
2000
n \le 2000
n≤2000 的数据来说太慢了,需要优化。
\newline
\newline
\newline
\newline
优化的动态规划
上面的状态转移方程需要比较所有的 k , 0 ≤ k ≤ j k, 0 \le k \le j k,0≤k≤j,但是这些 k k k 都是连续的,所以我们可以考虑像数组前缀和那样,定义一个前缀累积的状态
我们重新定义状态 d p [ i ] [ j ] dp[i][j] dp[i][j] 为 A ′ A' A′中的第 i i i 个数字 A ′ [ i ] A'[i] A′[i] 在 B [ 0 ] , B [ 1 ] , B [ 2 ] , . . . B [ j ] B[0], B[1], B[2], ... B[j] B[0],B[1],B[2],...B[j] 中时,最小的目标和
- 如果 A ′ [ i ] ≠ B [ j ] A'[i] \neq B[j] A′[i]=B[j],结果为 d p [ i ] [ j − 1 ] dp[i][j - 1] dp[i][j−1]
- 如果 A ′ [ i ] = B [ j ] A'[i] = B[j] A′[i]=B[j],结果为 d p [ i − 1 ] [ j ] + ∣ A [ i ] − B [ j ] ∣ dp[i - 1][j] + |A[i] - B[j]| dp[i−1][j]+∣A[i]−B[j]∣
此时状态转移方程为:
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
−
1
]
,
d
p
[
i
−
1
]
[
j
]
+
∣
A
[
i
]
−
B
[
j
]
∣
)
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j] + |A[i] - B[j]|)
dp[i][j]=min(dp[i][j−1],dp[i−1][j]+∣A[i]−B[j]∣)
时间复杂度为
O
(
n
2
)
O(n^{2})
O(n2)。另外因为
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 只和
i
,
i
−
1
i, i - 1
i,i−1 相关,可以优化到一维数组。
\newline
\newline
import java.util.Arrays;
import java.util.Scanner;
public class Main {
private int solve(int[] A) {
int[] B = Arrays.copyOf(A, A.length);
Arrays.sort(B);
// Reverse value array so we can calculate non-increasing result.
int ans = helper(A, B);
int i = 0;
int j = A.length - 1;
while (i < j) {
int tmp = B[i];
B[i] = B[j];
B[j] = tmp;
i++;
j--;
}
ans = Math.min(ans, helper(A, B));
return ans;
}
private int helper(int[] A, int[] B) {
int N = A.length;
int[] dp = new int[N];
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
int val = dp[j] + Math.abs(A[i] - B[j]);
if (j > 0) {
val = Math.min(dp[j - 1], val);
}
dp[j] = val;
}
}
return dp[N - 1];
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
int[] A = new int[N];
for (int i = 0; i < N; ++i) {
A[i] = scanner.nextInt();
}
Main main = new Main();
System.out.println(main.solve(A));
}
}