1. 程式人生 > >hdu3507 斜率優化dp入門

hdu3507 斜率優化dp入門

剛開始接觸斜率優化dp,做的一道簡單的入門題目,這裡有一篇論文,前半部分講的就是這個淺談數形結合思想在資訊學競賽中的應用
分析:
我們假設k<j<i。如果在j的時候決策要比在k的時候決策好,那麼也就是
dp[j]+M+(sum[i]sum[j])2<dp[k]+M+(sum[i]sum[k])2
(因為是最小花費嘛,所以優就是小於)兩邊移項一下,得到:
(dp[j]+sum[j]2(dp[k]+sum[k]2))/(2(sum[j]sum[k]))<sum[i]
我們把dp[j]sum[j]2看做是yj,把2sum[j]看成是xj
得到(y

jyk)/(xjxk)<sum[i]左邊為斜率的表示
定義F(j,k)=(yjyk)/(xjxk)

關鍵的來了:現在從左到右,還是設k<j<i,如果F[i,j]<F[j,k],那麼j點便永遠不可能成為最優解,可以直接將它踢出我們的最優解集。為什麼呢?

我們假設F[i,j]<sum[i],那麼就是說i點要比j點優,排除j點。
如果F[i,j]>=sum[i],那麼j點此時是比i點要更優,但是同時#F[j,k]>F[i,j]>sum[i]#。這說明還有k點會比j點更優,同樣排除j點。

然後你會發現其實我們排除的其實就是在二維座標系當中上凸的點 也就是說我們維護一個下凸的折線就可以得到答案,
程式碼:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cstring>
#define clr(x) memset(x,0,sizeof(x))
using namespace std;
#define LL long long
LL dat[500005]; LL dp[500005]; LL sum[500005]; LL GetUp(int i,int j) { return dp[i]+sum[i]*sum[i]-dp[j]-sum[j]*sum[j]; } LL GetDown(int i,int j) { return 2*sum[i]-2*sum[j]; } int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { clr(dp); clr(sum); int dque[500005]; int head,tail; head = tail = 0; dat[0] = 0; dque[tail++] = 0; for(int i = 1;i<=n;i++) { scanf("%lld",&dat[i]); sum[i] = dat[i]+sum[i-1]; while(head+1!=tail && (sum[i]*GetDown(dque[head+1],dque[head])>=GetUp(dque[head+1],dque[head]))) head++; dp[i] = dp[dque[head]] + (sum[i]-sum[dque[head]])*(sum[i]-sum[dque[head]])+m; while(tail-1!=head && (GetUp(i,dque[tail-1])*GetDown(dque[tail-1],dque[tail-2])<=GetUp(dque[tail-1],dque[tail-2])*GetDown(i,dque[tail-1]))) tail--; dque[tail++] = i; } printf("%lld\n",dp[n]); } return 0; }