POJ 3666. Making the Grade 数学证明及动态规划解法

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(1N2,000) describing the elevation ( 0 ≤ A i ≤ 1 , 000 , 000 , 000 ) (0 \le A^{}_{i} \le 1,000,000,000) (0Ai1,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} | A1B1+A2B2+...+ANBN

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[i1] A [ i ] A[i] A[i],如果 A [ i − 1 ] A[i - 1] A[i1] > 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[i1] 就行,而不需要提高到 A [ i − 1 ] + 1 , A [ i − 1 ] + 2.... A[i - 1] + 1, A[i - 1] + 2 .... A[i1]+1,A[i1]+2....,因为提高到 A [ i − 1 ] A[i - 1] A[i1] 已经满足要求。同理我们也可以降低 A [ i − 1 ] A[i - 1] A[i1] 的值到 A ′ [ i − 1 ] = A [ i ] A'[i - 1] = A[i] A[i1]=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} a0a1......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<cjcj+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 xjtk

如果 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<cjcj+1....ck<ai+1x
如果我们把 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} xai<cjcj+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} m1m2,那么我们把 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=cjcj+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(cjai)m2(cjai)S

如果 m 1 ≥ m 2 m^{}_{1} \ge m^{}_{2} m1m2,那么我们把 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<cjcj+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=Sm1(ai+1ck)+m2(ai+1ck)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[i1][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]=min0kj(A[i]B[j]+dp[i1][k])
这个算法时间复杂度为 O ( n 3 ) O(n^{3}) O(n3),对于 n ≤ 2000 n \le 2000 n2000 的数据来说太慢了,需要优化。
\newline
\newline
\newline
\newline

优化的动态规划

上面的状态转移方程需要比较所有的 k , 0 ≤ k ≤ j k, 0 \le k \le j k,0kj,但是这些 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] 中时,最小的目标和

  1. 如果 A ′ [ i ] ≠ B [ j ] A'[i] \neq B[j] A[i]=B[j],结果为 d p [ i ] [ j − 1 ] dp[i][j - 1] dp[i][j1]
  2. 如果 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[i1][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][j1],dp[i1][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,i1 相关,可以优化到一维数组。
\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));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aliengod

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值