斜率優化一般用於優化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 如果把右邊...