POJ 3612 DP

题意:给出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;
}


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值