「kuangbin帶你飛」專題二十 斜率DP
layout: post
title: 「kuangbin帶你飛」專題二十 斜率DP
author: "luowentaoaa"
catalog: true
tags:
mathjax: true
- kuangbin
- 動態規劃
- 斜率DP
傳送門
A.HDU - 3507 Print Article
題意
就是輸出序列a[n],每連續輸出的費用是連續輸出的數字和的平方加上常數M
讓我們求這個費用的最小值。
題解
概率DP的入門題,把我搞得要死要活的。
首先dp[i]表示輸出前i個的最小費用 很簡單得得出一個方程
\[
dp[i]=min(dp[i],dp[j]+(sum[i]-sum[j])^2\\1<=j<i
\]
其中sum[i]表示數字的前i項和,但是這個方程的復雜度是n^2 所以這時候就要用到斜率優化 ,ps:個人感覺斜率DP都用到了隊列,來把前面絕對不優秀的項都出隊,這樣每次運算都只要在隊列中找就行,而且每個元素只有一次出隊和入隊 所以復雜度只有N
首先假設在算dp[i]的 ,k<j<i,並且J點比K點優秀
那麽
\[
dp[j]+(sum[i]-sum[j])^2+M<=dp[k]+(sum[i]-sum[k])^2+M
\]
對上面方程分解整理得:
\[
[(dp[j]+sum[j]^2)-(dp[k]+sum[k]^2)]÷2(sum[j]-sum[k])<=sum[i]
\]
註意正負號,不然會影響不等號的方向
另
\[
Y(x)=dp[x]+sum[j]^2\\X(x)=2×sum[j]
\]
於是上面的式子變成斜率表達式
\[
[Y(j)-Y(k)]/[X(j)-X(k)]<=sum[i]
\]
由於不等式右邊的sum[i]隨著i的增加而遞增
所以我們另
\[
g[j,k]=[Y(j)-Y(k)]/[X(j)-X(k)]
\]
1.如果上面的不等式成立 說明J比K優,而且隨著i的增加上述不等式一定是成立的,也就是對於以後的i來說J都比K優秀,所以K是可以淘汰的
2.如果
\[
g[J,K]>g[I,J]\\k<j<i
\]
那麽J是可以淘汰的
假設g[I,J]<sum[i] 就是I比J優秀,那麽J就沒存在的價值
相反,如果g[I,J]>sum[i] 那麽同樣有g[J,K]>sum[I] 那麽K比J優秀 所以J是可以淘汰的
所以這樣相當於維護一個下凸的圖形,斜率在增加,用隊列維護
ps:以上都是抄bin巨的博客
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pp pair<int,int>
const ll mod=998244353;
const int maxn=5e5+50;
const ll inf=0x3f3f3f3f3f3f3f3fLL;
int gcd(int a,int b){while(b){int t=a%b;a=b;b=t;}return a;}
int lcm(int a,int b){return a*b/gcd(a,b);}
int dp[maxn];
int sum[maxn];
int a[maxn];
int q[maxn];
int m,n;
int head,tail;
int getDP(int i,int j){
return dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]);
}
int getUP(int j,int k){
return dp[j]+sum[j]*sum[j]-(dp[k]+sum[k]*sum[k]);
}
int getDOWN(int j,int k){
return 2*(sum[j]-sum[k]);
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
while(cin>>n>>m){
for(int i=1;i<=n;i++)cin>>a[i];
memset(sum,0,sizeof(sum));
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
head=tail=0;
q[tail++]=0;
for(int i=1;i<=n;i++){
while(head+1<tail&&getUP(q[head+1],q[head])<=sum[i]*getDOWN(q[head+1],q[head]))
head++;
dp[i]=getDP(i,q[head]);
while(head+1<tail&&getUP(i,q[tail-1])*getDOWN(q[tail-1],q[tail-2])<=getUP(q[tail-1],q[tail-2])*getDOWN(i,q[tail-1]))
tail--;
q[tail++]=i;
}
cout<<dp[n]<<endl;
}
return 0;
}
「kuangbin帶你飛」專題二十 斜率DP