POJ - 3612 Telephone Wire【DP】

Time limit 1000 ms
Memory limit 65536 kB

Farmer John’s cows are getting restless about their poor telephone service; they want FJ to replace the old telephone wire with new, more efficient wire. The new wiring will utilize N (2 ≤ N ≤ 100,000) already-installed telephone poles, each with some heighti meters (1 ≤ heighti ≤ 100). The new wire will connect the tops of each pair of adjacent poles and will incur a penalty cost C × the two poles’ height difference for each section of wire where the poles are of different heights (1 ≤ C ≤ 100). The poles, of course, are in a certain sequence and can not be moved.

Farmer John figures that if he makes some poles taller he can reduce his penalties, though with some other additional cost. He can add an integer X number of meters to a pole at a cost of X2.

Help Farmer John determine the cheapest combination of growing pole heights and connecting wire so that the cows can get their new and improved service.

Input
  • Line 1: Two space-separated integers: N and C
  • Lines 2…N+1: Line i+1 contains a single integer: heighti
Output
  • Line 1: The minimum total amount of money that it will cost Farmer John to attach the new telephone wire.

翻译

最近,Farmer John的奶牛们越来越不满于牛棚里一塌糊涂的电话服务于是,她们要求FJ把那些老旧的电话线换成性能更好的新电话线。 新的电话线架设在已有的N(2 <= N <= 100,000)根电话线杆上, 第i根电话线杆的高度为height_i米(1 <= height_i <= 100)。电话线总是从一根电话线杆的顶端被引到相邻的那根的顶端 如果这两根电话线杆的高度不同,那么FJ就必须为此支付 C*电话线杆高度差(1 <= C <= 100)的费用。当然,你不能移动电话线杆,只能按原有的顺序在相邻杆间架设电话线。Farmer John认为 加高某些电话线杆能减少架设电话线的总花费,尽管这项工作也需要支出一定的费用。更准确地,如果他把一根电话线杆加高X米的话,他得为此付出X^2的费用。请你帮Farmer John计算一下,如果合理地进行这两种工作,他最少要在这个电话线改造工程上花多少钱。


题目分析

d p [ i ] [ j ] dp[i][j] dp[i][j]表示考虑前 i i i个电线杆,切第 i i i根高度为 j j j时的最小花费
首先容易想到一个 O ( n h 2 ) O(nh^2) O(nh2)的方程
d p [ i ] [ j ] = m i n (   d p [ i − 1 ] [ k ] + a b s ( j − k ) ∗ c   ) + ( j − h i ) 2 dp[i][j]=min(\ dp[i-1][k]+abs(j-k)*c\ )+(j-h_i)^2 dp[i][j]=min( dp[i1][k]+abs(jk)c )+(jhi)2

但是这样复杂度肯定爆炸
我们考虑把上述方程的绝对值分开考虑
d p [ i ] [ j ] = m i n (   d p [ i − 1 ] [ k ] + j ∗ c − k ∗ c ) ) + ( j − h i ) 2 dp[i][j]=min(\ dp[i-1][k]+j*c-k*c) )+(j-h_i)^2 dp[i][j]=min( dp[i1][k]+jckc))+(jhi)2 ( k &lt; = j ) (k&lt;=j) (k<=j)
d p [ i ] [ j ] = m i n (   d p [ i − 1 ] [ k ] + k ∗ c − j ∗ c ) ) + ( j − h i ) 2 dp[i][j]=min(\ dp[i-1][k]+k*c-j*c) )+(j-h_i)^2 dp[i][j]=min( dp[i1][k]+kcjc))+(jhi)2 ( j &lt; = k ) (j&lt;=k) (j<=k)
( j − h i ) 2 (j-h_i)^2 (jhi)2是定值, m i n ( ) min() min()里面得值可以直接用一个变量mi维护

即在求 d p [ i ] [ j ] dp[i][j] dp[i][j]得时候 j j j分别正序倒叙更新一次
正序更新开始前先令 m i = m i n ( d p [ i − 1 ] [ k ] − k ∗ c ) ( 1 &lt; = k &lt; = h i ) mi=min(dp[i-1][k]-k*c)(1&lt;=k&lt;=h_i) mi=min(dp[i1][k]kc)(1<=k<=hi)
正序 j = h i j=h_i j=hi开始到 j = 100 j=100 j=100都要更新,每次更新 d p [ i ] [ j ] dp[i][j] dp[i][j]先更新 m i = m i n ( m i , d p [ i − 1 ] [ j ] − j ∗ c ) mi=min(mi,dp[i-1][j]-j*c) mi=min(mi,dp[i1][j]jc)
倒叙 j = 100 j=100 j=100开始到 j = h i j=h_i j=hi都要更新,每次更新 d p [ i ] [ j ] dp[i][j] dp[i][j]先更新 m i = m i n ( m i , d p [ i − 1 ] [ j ] + j ∗ c ) mi=min(mi,dp[i-1][j]+j*c) mi=min(mi,dp[i1][j]+jc)

这样复杂度就降到了 O ( n ∗ h ) O(n*h) O(nh)


#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
#define sqr(x) ((x)*(x))

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=100010;
int n,c,ans=1e9;
int hi[maxn];
int dp[maxn][110];

int main()
{
    n=read();c=read();
    for(int i=1;i<=n;++i) hi[i]=read();
    
    memset(dp,67,sizeof(dp));
    for(int i=hi[1];i<=100;++i) 
    dp[1][i]=sqr(i-hi[1]);
    
    for(int i=2;i<=n;++i)
    {
        int mi=1e9;
        for(int k=1;k<hi[i];++k) mi=min(mi,dp[i-1][k]-c*k);
        for(int j=hi[i];j<=100;++j)
    	{
    		mi=min(mi,dp[i-1][j]-c*j);
            dp[i][j]=min(dp[i][j],mi+c*j+sqr(j-hi[i]));
        }
    	mi=1e9;
    	for(int j=100;j>=hi[i];--j)
    	{
    		mi=min(mi,dp[i-1][j]+c*j);
            dp[i][j]=min(dp[i][j],mi-c*j+sqr(j-hi[i]));
        }
    }
    
    for(int i=hi[n];i<=100;++i)
    ans=min(ans,dp[n][i]);
    printf("%d",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值