參考資料下文將以一道例題為引子,描述最簡單的斜率優化的一般過程。
你有一支由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 如果把右邊...