5 5 5 9 5 7 5
230
/*分析: 假设sum[i]表示前i项和 dp[i]表示前i项需要花费的最少值 则dp[i]=min((sum[i]-sum[j])^2+m+dp[j]);//j=0~i-1 =>dp[i]=min(sum[i]*sum[i]+2*sum[i]*sum[j]+sum[j]*sum[j]+m+dp[j]); 由于存在sum[i]*sum[j]和i有关的这一项,所以不可能用单调队列来维护 2*sum[i]*sum[j]+sum[j]*sum[j]+m+dp[j]的最小值 这个时候怎么办??根据题意我们是要寻找到最优的j 所以假设k<j<i 有2*sum[i]*sum[j]+sum[j]*sum[j]+m+dp[j]<2*sum[i]*sum[k]+sum[k]*sum[k]+m+dp[k] =>(sum[j]*sum[j]+dp[j]-(sum[k]*sum[k]+dp[k]))/(2*sum[j]-2*sum[k])<sum[i]; 令 yj=sum[j]*sum[j]+dp[j] yk=sum[k]*sum[k]+dp[k] xj=2*sum[j]; xk=2*sum[k] 所以=>(yj-yk)/(xj-xk)<sum[i]则表示j比k更优 是连接点j和点k的直线的斜率 相对于点i最优的是j, 则在0~j相对于i+1,i+2...n点j肯定是最优的,因为满足sum[i+x]>sum[i]>yj-yk)/(xj-xk) 则只需要继续判断j+1~i+x之间是否有点更优,有的话j就不用保存了,这个过程用单调队列实行 而对于点i应该添加到队列中 比较i与点q[tail-1],q[tail-1]与点q[tail-2]的斜率k1,k2, 若k1<=k2则将点q[tail-1]去除,因为后面肯定是先k1<sum[i+x] 也就是点i肯定比q[tail-1]更优 然后重复比较直到k1>k2 */ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <queue> #include <algorithm> #include <map> #include <cmath> #include <iomanip> #define INF 99999999 typedef long long LL; using namespace std; const int MAX=500000+10; int n,m; int dp[MAX],sum[MAX],q[MAX]; int GetY(int i,int j){ return sum[i]*sum[i]+dp[i]-(sum[j]*sum[j]+dp[j]); } int GetX(int i,int j){ return 2*(sum[i]-sum[j]); } int main(){ int x; while(~scanf("%d%d",&n,&m)){ int head=0,tail=0; q[tail++]=0;//这一步必须,因为可能前i个数全部作为一段才是最小值 for(int i=1;i<=n;++i){ scanf("%d",&x); sum[i]=sum[i-1]+x; while(head+1<tail && GetY(q[head+1],q[head])<=GetX(q[head+1],q[head])*sum[i])++head;//更新最优的点 dp[i]=(sum[i]-sum[q[head]])*(sum[i]-sum[q[head]])+m+dp[q[head]];//计算dp[i]的最小值 while(head+1<tail && GetY(i,q[tail-1])*GetX(q[tail-1],q[tail-2])<=GetY(q[tail-1],q[tail-2])*GetX(i,q[tail-1]))--tail; q[tail++]=i; } printf("%d\n",dp[n]); } return 0; }
hdu3507之斜率优化DP入门,布布扣,bubuko.com
原文:http://blog.csdn.net/xingyeyongheng/article/details/25912663