hdu 3507 斜率DP入门题

题意:输出序列a[n],每连续输出的费用是连续输出的数字和的平方加上常数M,让我们求这个费用的最小值(n=5e5)

5 5

5 9 5 7 5

230

样例解释:5^2+5   9^2+5  5^2+5   7^2+5  5^2+5    sum=230

设dp[i]表示输出到i的时候最少的花费,S[i]表示从a[1]到a[i−1]的数字和。注意这里为了方便起见前缀和与一般的有区别。 则有:

dp[i]=min{dp[j]+(S[i+1]−S[j])^2+M}(j<i)

下面给出斜率DP的优化解释:

dp[j]+(S[i+1]-S[j])^2+M<dp[k]+(S[i+1]-S[k])^2+M

dp[j]+S[i+1]^2-2S[i+1]S[j]+S[j]^2+M<dp[k]+S[i+1]^2-2S[i+1]S[k]+S[k]^2+M

dp[j]-dp[k]+S[j]^2-S[k]^2<2S[i+1](S[j]-S[k])

\frac{dp[j]-dp[k]+S[j]^2-S[k]^2}{S[j]-S[k]}<2S[i+1]

f(x)=dp[j]-S[x]^2

\frac{f(j)-f(k)}{S[j]-S[k]}<2S[i+1]

当且仅当j>k,且上式成立时,j对更新dp[i]k更新dp[i]

当一个数的dp值求完了,它的f值也跟着确定,我们就可以在空间中绘制出点(S[i],f[i])

这个点代表已经求出dp值的一个点。

g[j,k]=\frac{f(j)-f(k)}{S[j]-S[k]}

现在从左到右,设g[i,j]<g[j,k](k<j<i)k<j<i,那么j点便永远不可能成为最优解。为什么呢?

我们假设g[i,j]<2S[i+1],也就是i点比j点优,排除j点。

如果g[i,j]>=2*S[i+1],那么此时j点比i点更优,但是同时g[j,k]>g[i,j]>2*S[i+1]

说明还有k点比j点优。

k<j<i

由于我们排除了g[i,j]<g[j,k]的情况,所以整个有效点集呈现一种上凸性质,即k j的斜率要大于ji的斜率。

这样,从左到右,斜率之间就是单调递减的了。当我们的最优解取得在j点的时候,那么k点不可能再取得比j点更优的解了,于是k点也可以排除。换句话说,j点之前的点全部不可能再比j点更优了,可以全部从解集中排除。

从队头开始,如果已有元素a b c,当i点要求解时,如果g[b,a]<2*S[i],那么说明b点比a点更优,a点可以排除,于是a出队

那么当d要入队的时候,我们维护队列的上凸性质,即如果g[d,c]<g[c,b],那么就将c点删除。直到找到g[d,x]>=g[x,y]为止,并将d点加入在该位置中。


#include<bits/stdc++.h>
using namespace std;
 
#define ll long long
int n,m;
ll pre[500005],dp[500005];
int v[500005],q[500005];
ll f(int i){
	return dp[i]+pre[i]*pre[i];
}
ll fenmu(int j,int k){
	return (pre[j]-pre[k]);
}
ll fenzi(int j,int k){
	return (f(j)-f(k)); 
}
int main(){
  while(cin>>n>>m){
    int head=0,tail=-1;
	pre[0]=0;
	for(int i=1;i<=n;i++){
		scanf("%d",&v[i]);
		pre[i]=pre[i-1]+v[i];
	}
	q[++tail]=0;
	for(int i=1;i<=n;i++){
		while(head<tail&&fenzi(q[head+1],q[head])<=2*pre[i]*fenmu(q[head+1],q[head])) ++head;
		dp[i]=dp[q[head]]+(pre[i]-pre[q[head]])*(pre[i]-pre[q[head]])+m;
		while(head<tail&&fenzi(i,q[tail])*fenmu(q[tail],q[tail-1])<=fenzi(q[tail],q[tail-1])*fenmu(i,q[tail])) --tail;
		q[++tail]=i;
	}
	cout<<dp[n]<<endl;
  }
}

猜你喜欢

转载自blog.csdn.net/qq_36553623/article/details/81391756