HDU-3507 Print Article(dp+斜率优化)
解题思路:
这道题的意思就是说输出一串数字,可以在不同行, cost 每行数字的平方+m, 求cost的最小值
这道题咋看一看dp 解就可以了 dp[i] = dp[j] + (sum[i]-sum[j])^2;很容易就能得出dp方程,但是时间复杂度是O(n^2),所以会爆。
所以需要优化它, 这种优化方法叫斜率优化(用斜率的形式得出谁是优解)
比如计算i 与 j,k 之间的优解,假设j 比k优
dp[j] + (sum[i]-sum[j])^2 <= dp[k] + (sum[i] -sum[k])^2;
移项可得: dp[j]+sum[j]^2-(dp[k]+sum[k]^2)/(2*(sum[j]-sum[k])<sum[i]①
令f(sum[x]) = dp[x]-sum[x];则有①可得 f(sum[j]) - f(sum[k])/2*(sum[j]-sum[k]) ②
当②<=sum[i] 是证明j比k优,否则k比j优;
我们可以借助这个性质,将一些不必要的点给剔除;
用g[j,k] 表示②那种形式
1. 如果g[j,k]<=sum[i], j比k优
2.如果g[j,k]<=g[i,j], 当g[i,j]<=sum[x] 时, i比j优,则j可以不要了,因为对于任意x,i都比j优
当g[i,j]>sum[x]时,j比i优,k比j优, 此时j也可以剔除,
此性质用来更新最优值队列,对于2的情况都可以将i代替j存入,此时可能会有j优于i 为什么还要替换它, 当j优于i的时候 还有k的存在,所以i和j的存在都是多余的,但是i代替j是g[i,j]<= sum[x] 时需要做的步骤, 所以直接保存,不需要再开分支;
//代码阶段分析
需要队列中的最优值的时候,因为每次插入当g[i,j]<= sum[x] 的情况 我们无法识别i和k谁最优,所以要再比较一下
由于插入队列的条件是getUp(que[tail-1],i)*getDown(que[tail-2],que[tail-1])<=getUp(que[tail-2],que[tail-1])*getDown(que[tail-1],i)
所以对于队列有存在 g[que[mid],i]>g[que[mid-1],que[mid]](这是退出循环的条件,此时i相当于que[mid+1])
可以分成2种情况①当g[que[mid],i] >sum[i] 时, que[mid-1]比que[mid]优,que[mid]比i优,则在队列中que[mid-1] 是最优的 不明白的自己再演算下
②当g[que[mid-1],que[mid]]<=sum[i] , que[mid]比que[mid-1]优,i比que[mid]优,则que[mid]与i 还需要再比较,直到到队列尾或者①的情况
#include<iostream> #include<cmath> #include<algorithm> #include<cstdio> using namespace std; int dp[500100],sum[500100]; int que[500100]; int n,m; int getDp(int i,int j){ return dp[j] + m + (sum[i]-sum[j])*(sum[i] - sum[j]); } int getUp(int k,int j){ return dp[j] + sum[j]*sum[j] - (dp[k] + sum[k]*sum[k]); } int getDown(int k,int j){ return 2*(sum[j] - sum[k]); } int main(){ int temp,head,tail; while(scanf("%d%d",&n,&m)!=EOF){ sum[0] = 0; for(int i=1;i<=n;i++){ scanf("%d",&temp); sum[i] = sum[i-1]+temp; } //for(int i=1;i<=n;i++) cout<<sum[i]<<endl; head = tail = 0; que[tail++] = 0; for(int i=1;i<=n;i++){ while(head+1<tail&&getUp(que[head],que[head+1])<=sum[i]*getDown(que[head],que[head+1])) head++; dp[i] = getDp(i,que[head]); while(head+1<tail&&getUp(que[tail-1],i)*getDown(que[tail-2],que[tail-1])<=getUp(que[tail-2],que[tail-1])*getDown(que[tail-1],i)) tail--; que[tail++] = i; } printf("%d\n",dp[n]); } return 0; }