题意:有一列树,总共有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; }