poj 3612 dp

题意:有一列树,总共有n(n<=100000)棵,给定树的高度(h<=1000),要在相邻树和树之间架设电线,代价为c*(两树的高度差的绝对值)。但可以增高树的高度,增高需要的代价为:增高高度的平方。求最小代价。

思路:一个n*h*h的dp很容易想到,即dp[i][j]=min(dp[i][k]+abs(j-k)+(j-s[i])^2) ,dp[i][j]表示考虑前i棵树,第i棵高度为j的最小代价。但是要优化:把前面的转移方程变形,把涉及到k和不涉及k的分开,所以写成:

1、j>=k : dp[i][j] = min(dp[i-1][k] -k*c) + (j-s[i])^2+j*c;

2、j<=k : dp[i][j] = min(dp[i-1][k]+k*c) +(j-s[i])^2-j*c。

只要预处理求出前面的项,存入数组即可。这样n*h的复杂度即可。

代码中的m表示高度的最大值,因为不会有树的高度超过最大值。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 100005
int dp[N][105],f[1002];
int s[N],c,n,m,low[105],high[105];
void init(){
    for(int i = 0;i<=1000;i++)
        f[i] = i*i;
}
int main(){
    init();
    while(scanf("%d %d",&n,&c)!=EOF){
        int i,j,res=INF;
        for(i = 1;i<=n;i++){
            scanf("%d",&s[i]);
            m = max(m,s[i]);
        }
        for(j = s[1];j<=m;j++)
            dp[1][j] = f[j-s[1]];
        for(i = 2;i<=n;i++){
            for(j = 0;j<s[i-1];j++)
                low[j] = INF;
            for(j = s[i-1];j<=m;j++)
                low[j] = min(low[j-1],dp[i-1][j]-j*c);
            high[m] = dp[i-1][m]+m*c;
            for(j = m-1;j>=s[i-1];j--)
                high[j] = min(high[j+1],dp[i-1][j]+j*c);
            for(j = s[i-1]-1;j>=1;j--)
                high[j] = high[j+1];
            
            for(j = s[i];j<=m;j++)
                dp[i][j] = min(low[j]+j*c ,high[j]-j*c)+f[j-s[i]];
        }
        for(i = s[n];i<=m;i++)
            res = min(res,dp[n][i]);
        printf("%d\n",res);
    }
    return 0;
}


你可能感兴趣的:(poj 3612 dp)