题意:给出n根电线杆的高度和参数C,农夫约翰要在电线杆之间搭电话线,相邻两根电线杆搭线的花费为
|h[i] - h[i+1]| * C, 现可以将电线杆抬高,抬高的的花费为x * x,其中x为抬高的高度。
思路:设dp[i][j]为前i根电线杆,第i根高度为j时的最小花费,则易得转移方程:
dp[i][j] = min{dp[i-1][k] + C *|j - k| + (j - h[i]) ^ 2}, j >= h[i], k >= h[i-1];
时间复杂度O(n*h*h),考虑优化。
将转移方程中绝对值拆开,可得:
dp[i][j] = C * j + (j - h[i]) ^ 2 + min{dp[i-1][k] - C * k}, j >= k,
dp[i][j] = -C * j + (j - h[i]) ^ 2 + min{dp[i-1][k] + C * k}, j < k.
容易发现,前半部分可直接算,后半部分可预处理得到,不妨设:
low[j] = min{dp[i-1][k] - C * k}, k <= j;
high[j] = min{dp[i-1][k] + C * k}, k > j;
新的转移方程为:
dp[i][j] = C * j + (j - h[i]) ^ 2 + min(low[j], hign[j]);
low与high都可在O(h)时间内推出,故复杂度为O(n * h).
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#define For(i,j,k) for(int i = j;i <= k;i ++)
#define Forr(i,j,k) for(int i = j;i >= k;i --)
#define Set(c, val) memset(c, val, sizeof(c))
#define Debug(x) printf("%s = %d\n", #x, x)
#define sqr(x) ((x) * (x))
using namespace std;
const int H = 110;
int dp[H], low[H], high[H], n, h, C;
int main(){
freopen("telewire.in", "r", stdin);
freopen("telewire.out", "w", stdout);
low[101] = high[0] = 1e9;
scanf("%d%d", &n, &C);
For(i,1,n){
scanf("%d", &h);
Set(dp, 0x3f);
For(j,h,100)
dp[j] = sqr(j - h) + (i == 1 ? 0 : min(high[j] + j * C, low[j] - j * C));
Forr(j,100,1) low[j] = min(low[j+1] , dp[j] + j * C);
For (j,1,100) high[j] = min(high[j-1], dp[j] - j * C);
}
int Ans = 1e9;
For(i,h,100) Ans = min(Ans, dp[i]);
printf("%d\n", Ans);
return 0;
}