斜率優化小記

2022-04-29 04:51:09 字數 4464 閱讀 7387

參考資料

下文將以一道例題為引子,描述最簡單的斜率優化的一般過程。

你有一支由n名士兵組成的部隊,士兵從1到n編號,要將他們拆分成若干個特別行動隊調入戰場。

出於默契的考慮,同一支行動隊的隊員的編號應該連續。

編號為i的士兵的初始戰鬥力為xixi,一支行動隊的初始戰鬥力為隊內所有隊員初始戰鬥力之和。

通過長期觀察,你總結出一支特別行動隊的初始戰鬥力x將按如下公式修正為x』:

\[x′=ax^2+bx+c

\]其中,a,b,c是已知的係數(a<0)。

作為部隊統帥,你要為這支部隊進行編隊,使得所有特別行動隊修正後的戰鬥力之和最大。

試求出這個最大和。

定義 \(f[i]\) 為劃分 x1...xi 完畢後能獲得的戰鬥力之和的最大值。

列舉上乙個劃分點 \(j\) ,易得

\[f[i]=\max _ \

\]單獨的考慮 \(i\) 以 \(j\) 作為決策點的情況。

\(f[i]=f[j]+a*(s[i]-s[j])^2+b*(s[i]-s[j])+c\)

整理成 \(y=kx+b\) 的形式。

所有含有 \(j\) 項和常量放在左邊,作為 \(y\) 。

所有含有 \(i\) 的項,放在右邊,作為 \(b\).

只剩餘 類似 \(c \times g(i)\times g(j)\) 的項,把項變到右邊,

如果 \(x\) 的表示式單調遞減,等式兩邊同乘 −1 變為單增。

上述規則參考了斜率優化dp複習筆記的有關部分,建議閱讀原文。

同時這個步驟和高中的線性規劃也有很多相似之處。

整理完畢: \((f[j]+a*s[j]^2-bs[j]+c)=(2*a*s[i])*s[j]+(f[i]-a*s[i]^2-b*s[i])\).

k已知,那麼對於確定的 \(j\) , \(b\) 也唯一確定。

由於要求 \(f[i]\) 的 max ,也就是要讓 \(b\) (幾何意義是截距)最大。

以下是(我假想的) 每個\(f[j]\) 對應的點 \((x=s[j],y=f[j]+a*s[j]^2-bs[j]+c).\) 在平面上的圖。

想像一條斜率 \(k=2*a*s[i]\) 的直線由上方落下,直到碰上第乙個點,取得 \(b_.\)

只有上凸包的點有用。

不妨只保留這些點,維護乙個上凸包。

每次斜率為 \(k\) 的直線只會和 上凸包上第乙個斜率 \(的直線碰上。

又觀察到本題有特殊的性質,即 \(k=2*a*s[i]\) 單調遞減,那對於上凸包上斜率 $ \geq k$ 線段,這次碰不上,以後也不會碰到了,所以我們把這些線段刪除,直到斜率小於k,取此時的點為決策點進行轉移。

把 \(i\) 加入決策集合時,如果 \(slope(i,q[tt])>slope(q[tt],q[tt-1])\),那麼說明 $i $ 在上凸包的上方,\(q[tt]\),這個點就沒用了,刪除並繼續。

下面給出code:

#include#include#include#include#include#include#include#include#include#include#include#include#includeusing namespace std;

typedef long long ll;

typedef long double ld;

typedef unsigned long long ull;

const int n=1e6+5;

int n;

ll a,b,c;

ll s[n],f[n];

// f[j]-a*s[j]*s[j]-b*s[j]+c = (2*a*s[i]) * (s[j]) + (f[i]-a*s[i]*s[i]-b*s[i]);

// y=kx+b, k= 2*a*s[i] 單調遞減。

// max ---> 維護乙個上凸包 ---> 兩點斜率單調遞減。

inline ld x(int j)

inline ld y(int j)

inline bool cmp(int i,int j,int k)

int q[n];

int main()

int hh=0,tt=0;

q[hh]=0;

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

double y(int j)

inline bool cmp(int i,int j,int k)

int q[n];

int main()

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

s[i]+=s[i-1],c[i]+=c[i-1];

memset(f,0x3f,sizeof f);

f[0]=0;

int hh=0,tt=0;

q[hh]=0;

for (i=1; i=i*(x(q[hh])-x(q[hh+1]))) hh++;

j=q[hh];

f[i]=f[j]+(c[i]-c[j])*i-(s[i]-s[j]);

j=i+1-m;

while(hh=maxt) ans=min(ans,f[i]);

}cout

#include#include#includeusing namespace std;

typedef long long ll;

const int n=3e5+5;

ll f[n];

ll sc[n],st[n],t[n],c[n];

int p[n];

int n;

ll s;

ll x(int j)

ll y(int j)

double slope(int i,int j)

int q[n];

int main()

memset(f,0x3f,sizeof f);

f[0]=0;

int hh=0,tt=0;

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

cout運輸小貓:

#include#include#include#include#include#include#include#include#include#include#include#include#includeusing namespace std;

typedef long long ll;

typedef double ld;

typedef unsigned long long ull;

const int n=1e5+5,p=105;

ll f[n][p];

ll d[n],a[n],t[n],s[n];

int h[n];

int q[n];

int n,m,p;

int cur;

inline ld x(int j)

inline ld y(int j)

inline bool comp(int i,int j,int k)

int main()

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

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

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

s[i]=s[i-1]+a[i];

memset(f,0x3f,sizeof f);

f[0][0]=0;

int hh,tt;

for(cur=0,j=1;j<=p;cur++,j++)

inline ll y(int j)

inline bool cmp(int i,int j,int k)

// 斜率 k=st 不是遞增的,但 x=sc 單調不降。

// 所以每次插入時仍從最後插入,但 查詢決策點需要二分。

int q[n];

int main()

memset(f,0x3f,sizeof f);

f[0]=0;

int tt=0,l,r,mid;

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

j=q[r];

f[i]=f[j]+(sc[n]-sc[j])*s+(sc[i]-sc[j])*st[i];

while(tt>0 && cmp(q[tt-1],q[tt],i) ) tt--;

q[++tt]=i;

}cout平衡樹或 cdq 分治。

Dp優化 斜率優化

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

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

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 如果把右邊...