我要长高
http://www.acm.uestc.edu.cn/problem.php?pid=1685
韩父有N个儿子,分别是韩一,韩二…韩N。由于韩家演技功底深厚,加上他们间的密切配合,演出获得了巨大成功,票房甚至高达2000万。舟子是名很有威望的公知,可是他表面上两袖清风实则内心阴暗,看到韩家红红火火,嫉妒心遂起,便发微薄调侃韩二们站成一列时身高参差不齐。由于舟子的影响力,随口一句便会造成韩家的巨大损失,具体亏损是这样计算的,韩一,韩二…韩N站成一排,损失即为C*(韩i与韩i+1的高度差(1<=i<N))之和,搞不好连女儿都赔了.韩父苦苦思索,决定给韩子们内增高(注意韩子们变矮是不科学的只能增高或什么也不做),增高1cm是很容易的,可是增高10cm花费就很大了,对任意韩i,增高Hcm的花费是H^2.请你帮助韩父让韩家损失最小。
有若干组数据,一直处理到文件结束。 每组数据第一行为两个整数:韩子数量N(1<=N<=50000)和舟子系数C(1<=C<=100) 接下来N行分别是韩i的高度(1<=hi<=100)。
首先建立方程,很容易想到的是,dp[i][j]表示第 i 个儿子身高为 j 的最低花费。分析题目很容易知道,当前儿子的身高花费只由前一个儿子影响。因此,
dp[i][j]=min(dp[i-1][k] + abs(j-k)*C + (x[i]-j)*(x[i]-j));其中x[i]是第i个儿子原本的身高
我们分析一下复杂度。
首先有N个儿子,这需要一个循环。再者,每个儿子有0到100的身高,这也需要一维。再再者,0到100的每一个身高都可以有前一位儿子的身高0到100递推而来。
所以朴素算法的时间复杂度是O(n^3)。题目只给两秒,难以接受!
分析方程:
当第 i 个儿子的身高比第 i-1 个儿子的身高要高时,
dp[i][j]=min(dp[i-1][k] + j*C-k*C + X); ( k<=j ) 其中 X=(x[i]-j)*(x[i]-j)。
当第 i 个儿子的身高比第 i-1 个儿子的身高要矮时,
dp[i][j]=min(dp[i-1][k] - j*C+k*C + X); ( k>=j )
对第一个个方程,我们令 f[i-1][k]=dp[i-1][k]-k*C, g[i][j]=j*C+X; 于是 dp[i][j] = min (f[i-1][k])+ g[i][j]。转化成这样的形式,我们就可以用单调队列进行优化了。
第二个方程同理。
接下来便是如何实现,实现起来有点技巧。具体见下
代码:
#include<iostream> #include<cstdio> #define INF 0xfffffff #define min(a,b) a<b?a:b using namespace std; int q[101],dp[2][101]; int main() { int n,c,x,nowf,head,tail,cur; while(scanf("%d%d",&n,&c)!=EOF) { cur=0;//高度最大为100 //以第一个人的身高初始化 scanf("%d",&x); for(int i=0; i<x; i++) { dp[cur][i]=INF;//标记不可能状态 } for(int i=x; i<=100; i++) { dp[cur][i]=(i-x)*(i-x); } for(int i=1; i<n; i++) { scanf("%d",&x); cur=1-cur;//现在cur指向后一个人 head=tail=0; for(int j=0; j<=100; j++) //比前一个人高,前面已经存了0~j-1 { nowf=dp[1-cur][j]-c*j; while(head<tail&&nowf<q[tail-1]) { tail--; } q[tail++]=nowf; if(j<x) { dp[cur][j]=INF; } else { dp[cur][j]=q[head]+c*j+(x-j)*(x-j); } } head=tail=0; for(int j=100; j>=0; j--) //比前一个人矮,前面存了j+1~100 { nowf=dp[1-cur][j]+c*j; while(head<tail&&nowf<q[tail-1]) { tail--; } q[tail++]=nowf; if(j>=x) { dp[cur][j]=min(dp[cur][j],q[head]-c*j+(x-j)*(x-j)); } } } int ans=INF; for(int i=0; i<=100; i++) { ans=min(ans,dp[cur][i]); } printf("%d\n",ans); } return 0; }