斜率優化系列 訓練記錄

2022-03-29 00:31:21 字數 4294 閱讀 1336

斜率優化一般用於優化dp的轉移,藉著訓練斜率優化的相關問題來提公升一些dp思維。選擇老學長留下的專題場來練手,由於該場題數較多,以及個人不太願意長時間進行單一專題訓練,因此開此文來記錄斷續的訓練結果和心得。

由一道簡單入門題玩具裝箱開頭,題意和思路比較簡單就不講了。

**

#include#define dd(x) cout<<#x<<" = "typedef priority_queuebq;

typedef priority_queue,greater> sq;

const int maxn=1e5+10,inf=0x3f3f3f3f,mod=1e9+7;

ll f[maxn],sum[maxn],a[maxn],q[maxn],h,t;

inline double k(int i,int j)

int main()

a[0]=1;

h=t=1;

for (int i=1;i<=n;++i)

cout《小a與最大欄位和還是入門題。題意見原題面,比較簡單。思路的話,維護乙個普通字首和與乙個梯形字首和,然後與上題一樣通過變形式子寫成直線截距式。唯一的不同是,該題ai可能是負數,因此直線的斜率不能保證單調製化,因此選取最優點時需要二分佇列找到首個往後比直線斜率小的點。(上一題由於直線斜率單調增加,因此每次選最優點只要把隊首比直線斜率小的點都出隊即可)。然後這題要最大值,所以入隊時要維護乙個上凸殼。

**#include#define dd(x) cout<<#x<<" = "typedef priority_queuebq;

typedef priority_queue,greater> sq;

const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1e9+7;

ll s1[maxn],s2[maxn],q[maxn],h,t;

inline double k(int i,int j)

int find(double k)

if (k(q[m],q[m+1])<=k)

ans=m,r=m-1;

else

l=m+1;

} return ans;

}int main()

ll ans=-1e18;

h=t=1;

for (int i=1;i<=n;++i)

cout題意是給個長為n的數列,找乙個長度不小於k且平均值最大的子段。換句話說就是找所有點(i,sum[i])中斜率最大的兩個點的斜率。

不難想到,我們應該維護乙個下凸殼(因為其上方的點肯定無法與之後的點構成更優的解),然後一般可以二分找最優點,但是由這題的性質可以發現,因為sum[i]是遞增的,因此每次找到更優的點時,其前面的點可以直接捨棄(它們不可能與後面新加入的點構成更大的斜率了)。因此複雜度可以做到o(n)。

**(再次強調,不建議做)

#include#define dd(x) cout<<#x<<" = "typedef priority_queuebq;

typedef priority_queue,greater> sq;

const int maxn=1e5+10,inf=0x3f3f3f3f,mod=1e9+7;

struct fastio

inline int xchar()

inline int xint()

~fastio()

} io;

ll sum[maxn];

int q[maxn],h,t;

inline double k(int i,int j)

int main()

h=0;

t=-1;

double ans=-1;

for (int i=k;i<=n;++i)

ll dx(int i,int j)

ll getf(int i,int j)

int main()

(0<=i

#include#include#include#define dd(x) cout<<#x<<" = "const int maxn=1e5+10,mod=1e9+7,inf=0x3f3f3f3f;

ll fsum[maxn],tsum[maxn],dp[maxn];

int q[maxn],h,t,s,n;

inline ll dy(int i,int j)

inline ll dx(int i,int j)

inline ll getv(int i,int j)

int main()

t=h=0;

for (int j=1;j<=n;++j)

inline ll dx(int i,int j)

inline ll getf(int i,int j,int k)

int main()

return 0;

}

hdu 2829,與上題乙個型別,這題定義乙個區間的價值為其中任意兩個數乘積的和。在區間價值的表示式上想了比較久,區間[i,j]的價值可以表示為c[j]-c[i]-sum[i]*(sum[j]-sum[i]),c[i]表示字首i的價值,想出來之後就是跟上題一樣的處理,沒什麼坑點,注意邊界就行。

**

#include#define dd(x) cout<<#x<<" = "typedef priority_queuebq;

typedef priority_queue,greater> sq;

const int maxn=1e3+10,mod=1e9+7,inf=0x3f3f3f3f;

ll f[maxn][maxn],a[maxn],sum[maxn],c[maxn];

int q[maxn],h,t;

inline ll dy(int i,int j,int k)

inline ll dx(int i,int j)

inline ll getf(int i,int j,int k)

int main()

for (int i=2;i<=n;++i)

c[i]=c[i-1]+sum[i-1]*a[i];

for (int j=1;j<=n;++j)

f[1][j]=c[j];

for (int k=2;k<=m;++k)

inline ll dx(int i,int j)

inline ll getf(int i,int j)

int main()

h=t=0;

for (int i=1;i<=n;++i)

inline ll dx(int i,int j)

inline ll getf(int i,int j)

int main()

h=t=0;

for (int i=k;i<=n;++i)

inline ll dx(int i,int j)

inline ll getf(int i,int j,int k)

int main()

m=min(m,cnt);

for (int i=1;i<=cnt;++i)

f[1][i]=a[i].fi*a[1].se;

for (int k=2;k<=m;++k)

ll dx(int i,int j)

ll getf(int i,int j,int k)

int main()

for (int i=1;i<=m;++i)

sort(a+1,a+1+m);

for (int i=1;i<=m;++i)

for (int k=2;k<=p;++k)

double find(int i)

if (sign(dy(q[m+1],q[m])-k*dx(q[m+1],q[m]))>=0)

p=m,r=m-1;

else

l=m+1;

} return a(q[p])*a[i]-b(q[p])*b[i];

}void cdq(int l,int r)

int m=(l+r)>>1;

cdq(l,m);

int tn=0;

for (int i=l;i<=m;++i)

tmp[tn++]=i;

sort(tmp,tmp+tn,cmp);

t=0;

for (int i=0;i>n>>s;

for (int i=1;i<=n;++i)

scanf("%lf%lf%lf",&a[i],&b[i],&r[i]);

f[1]=s;

cdq(1,n);

printf("%.3lf",f[n]);

return 0;

}

Dp優化 斜率優化

該模板的ai要具有單調性,也就是sum陣列 若沒有單調性,加個二分即可 我的一篇題解,很詳細 1 寫出狀態轉移方程 2 按照斜率優化的式子推導出式子 3 代入模板即可。include include using namespace std typedef long long ll const int...

學渣亂搞系列之dp斜率優化

學渣亂搞系列之dp斜率優化 by 狂徒歸來 下面看乙個例題print article。連續列印一段長達從i到j的的文章,需要m sigma sum j sum i 2的費用,求列印完這篇文章的最小費用。到底分多少次去列印,費用最低呢。dp i 表示列印從1.i的最小費用。那麼我們有dp轉移方程dp ...

斜率優化 DP

我們知道,有些dp方程可以轉化成dp i f j x i 的形式,其中f j 中儲存了只與j相關的量。這樣的dp方程我們可以用單調佇列進行優化,從而使得o n 2 的複雜度降到o n 可是並不是所有的方程都可以轉化成上面的形式,舉個例子 dp i dp j x i x j x i x j 如果把右邊...