Telephone Wire [USACO07NOV] 洛谷P2885 [单调队列优化dp]
Description
最近,约翰的奶牛们越来越不满足于牛棚里一塌糊涂的电话服务,于是,她们要求约翰把那些老旧的电话线换成性能更好的新电话线。
新的电话线架设在己有的n根电话线杆上,第i根电话线的高度为hi, ( 1 <= hi<= 100)。电话线总是从一根电话线杆的顶端到相邻的那根的顶端,如果这两根电话线杆的高度hi和hj不同,那么约翰就必须支付c * |hi - hj|的费用,当然,你不能移动电话线杆,只能按照原有的顺序在相邻杆间架设电话线。
加高某些电话线杆能减少架设电话线的总费用尽管这项工作也需要支付一定的费用。更准确的说,如果他把一根电话线杆加高x米的话,他需要付出x^2费用。
请你帮约翰计算一下,如果合理的进行这两项工作,他最少要在这个电话线改造
工程中花多少钱。
Input
第一行输入两个数n和c, 含义如上
接下来n行,每行一个整数hi
【数据范围】
N <= 100000, C <= 100, Hi < 100,答案不超过maxlongint。
Output
输出约翰完成电话线改造工程需要花费的最小费用
Sample Input
5 2
2
3
5
1
4
Sample Output
15
分析
首先考虑一般的dp状态转移
由于花费与高度相关 所以要有第二维表示高度
定义dp[i][j]为前i根电线杆 且第i根高度为j时的最小花费
此时的状态转移方程:
dp[i][j]=min(dp[i-1][k]+abs(j-k)*c)+(j-h[i]) * (j-h[i])
k是需要枚举的上一根电线杆的高度
那这样就需要枚举三维 肯定过不了这么大的数据
分析状态转移方程,我们先把绝对值拆开
①
j>=k时
dp[i][j]
=min(dp[i-1][k]+(j-k) * c)+(j-h[i]) * (j-h[i])
=min(dp[i-1][k]-k * c)+ (j-h[i]) * (j-h[i])+j*c
min里面的部分就只与k有关 那么就可以用单调队列优化
②
同理
j<k时
dp[i][j]
=min(dp[i-1][k]+(k-j) * c)+ (j-h[i]) * (j-h[i])
=min(dp[i-1][k]+k * c)+(j-h[i]) * (j-h[i])- j * c
分成两种情况分别更新即可
事实上 采用合理的遍历顺序 我们根本不需要用单调队列来维护 只需要一个变量就可以
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 100005
#define MAXH 100
#define INF 0x3f3f3f3f
int n,c,h[MAXN],dp[MAXN][MAXH+5];
int main()
{
scanf("%d %d",&n,&c);
for(int i=1;i<=n;i++)
scanf("%d",&h[i]);
memset(dp,0x3f,sizeof(dp));
int minx=INF;
for(int j=h[1];j<=MAXH;j++)
dp[1][j]=(j-h[1])*(j-h[1]);
for(int i=2;i<=n;i++)
{
minx=INF;
for(int k=1;k<h[i];k++)
minx=min(minx,dp[i-1][k]-k*c);
for(int j=h[i];j<=100;j++)
{
minx=min(minx,dp[i-1][j]-j*c);
dp[i][j]=min(dp[i][j],minx+(j-h[i])*(j-h[i])+j*c);
}
minx=INF;
for(int j=100;j>=h[i];j--)
{
minx=min(minx,dp[i-1][j]+c*j);
dp[i][j]=min(dp[i][j],minx+(j-h[i])*(j-h[i])-j*c);
}
}
int ans=INF;
for(int i=h[n];i<=100;i++)
ans=min(ans,dp[n][i]);
printf("%d\n",ans);
return 0;
}