解题思路
首先很容易就想到了一个二维的朴素的 $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; }
原文地址:https://www.cnblogs.com/bljfy/p/9582353.html
时间: 2024-10-31 06:20:21