「 COGS 2240 」 X 「 Luogu P2885 」 架设电话线

解题思路

首先很容易就想到了一个二维的朴素的 $dp$。

设 $dp[i][j]$ 表示第 $i$ 个位置的电话线杆的高度为 $j$ 时的最小花费,就需要枚举第 $i$ 个电话线杆、第 $i$ 个电话线杆的高度 $j$、第 $i-1$ 个电话线杆的高度 $k$。

状态转移方程如下

$$dp[i][j] = \min \{dp[i-1][k]+|j-k|\times c + (j-h[i])^2\}$$

但是这样的 $dp$ 过不了这题的数据范围。这个 $dp$ 的时间复杂度是 $\text{O}(n\times h^2)$。

所以需要考虑别的方法进行优化。

我们试着只枚举一个 $j$,而不是枚举 $j$ 和 $k$。

首先来说这个 $j$,它既是我们枚举的第 $i$ 个电话线杆的高度也是我们枚举的第 $i-1$ 根电话线杆的高度。$i-1$ 要相对 $i$ 产生影响,那么 $j$ 一定是大于 $h[i-1]$ 的。

我们在 $j\ge h[i-1]$ 时取一个最小值,同时又要消去绝对值的影响。这就要看 $j$ 的枚举顺序了。如果是正序枚举那么之后的j一定会大于当前的j,之后的 $j-$ 现在的 $j$,是正的。

所以现将现在的 $j\times c$ 减掉。到时候进行扩展的时候在将那时的 $j\times c$ 加上。就等价于加上了绝对值。倒序枚举只是换了下顺序,道理还是一样的就不再过多的解释。

再来看 $j\ge h[i]$ 的时候,这时的高度为 $j$ 的第 $i$ 根电话线杆已经能够被更新了。所以就将其更新。

然后再做一遍倒序枚举因为还要考虑 $i-1$ 比 $i$ 高的情况。

附上代码

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
inline int read() {
    int x = 0, f = 1; char c = getchar();
    while (c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while (c <= '9' && c >= '0') {x = x*10 + c-'0'; c = getchar();}
    return x * f;
}
const int maxn = 1e5+3, INF = 1e9;
int n, c, h[maxn], f[maxn][103], Ans = INF;
int main() {
    freopen("phonewire.in", "r", stdin);
    freopen("phonewire.out", "w", stdout);
    n = read(), c = read();
    for(int i=1; i<=n; i++)
        for(int j=0; j<=100; j++)
            f[i][j] = INF;
    for(int i=1; i<=n; i++)
        h[i] = read();
    for(int i=h[1]; i<=100; i++) f[1][i] = (i-h[1]) * (i-h[1]);
    int minn;
    for(int i=2; i<=n; i++) {
        minn = INF;
        for(int j=0; j<=100; j++) {
            if(j >= h[i-1]) minn = min(minn, f[i-1][j] - c*j);
            if(j >= h[i]) f[i][j] = min(f[i][j], minn + c*j + (j-h[i]) * (j-h[i]));
        }
        minn = INF;
        for(int j=100; j>=0; j--) {
            if(j >= h[i-1]) minn = min(minn, f[i-1][j] + c*j);
            if(j >= h[i]) f[i][j] = min(f[i][j], minn - c*j + (j-h[i]) * (j-h[i]));
        }
    }
    for(int i=0; i<=100; i++) Ans = min(Ans, f[n][i]);
    printf("%d", Ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/bljfy/p/9582353.html