斜率優化dp

2022-08-04 07:51:14 字數 3332 閱讀 9952

一種常見的優化dp的方法

首先dp要滿足求最優狀態[最大最小值],而不是方案數

然後根據轉移方程列出形如 \(b=y-k\times x\) 的方程

然後根據k的變化和x的變化,用單調佇列或者cdq分治之類的方法維護上下凸函式影象

舉乙個例子:

\(dp_i= max(dp_j-val_i*val_j+sum_j-sum_i)\)

那麼 \(b=dp_i+sum_i\) ,\(y=dp_j+sum_j\) ,\(x=val_j\) ,\(k=val_i\)

然後根據x,y在座標系上標點,然後畫斜率為k的直線使得之前經過座標系上的點,易知要維護乙個上凸函式

常見套路:

1.x有序,k有序

單調棧或者單調佇列維護函式即可 \(o(n)\)

2.x有序,k無序

用單調棧或者單調佇列維護函式,詢問時二分\(o(nlogn)\)

3.x無序

利用cdq分治,使得x有序 \(o(nlogn)-o(nlog^2n)\)

模板單調佇列[情況1]

struct slope_queue

void init()

int sz()

bool empty()

bool check(pll a,pll b,pll c)

bool check(pll a,pll b,ll k)

void insert(pll a)

void init()

bool check(pll a,pll b,pll c)

bool check(pll a,pll b,ll k)

void insert(pll x)

else l=mid+1;

} return a[res];

}}sq;

cdq分治
void solve(int l,int r)

solve(mid+1,r);//再處理右邊的

}

平面中有 n個點 (xi,yi) ,有 m 條直線,斜率 k 已經確定,需要在給定的 n 個點中,選出乙個點 (x,y) ,使得 kx+y最大。

solution

對於每個條直線,有 $ b=k\times x+y$

將式子化為 $b=y-k\times(-x) $

用單調佇列維護上凸包,把點按x從小到大排序後,依次插入佇列

然後將詢問的k排序[從大到小],根據k彈掉斜率大於當前點的

code

#includeusing namespace std;

#define for(i,x,y) for(int i=(x),i##_end=(y);i<=i##_end;++i)

#define x first

#define y second

typedef long long ll;

typedef pairpii;

const int m=100005;

pii a[m];

int q[m],id[m];

ll ans[m];

bool cmp(int a,int b)

struct slope_queue

bool chk(pii a,pii b,pii c)

bool chk(pii a,pii b,int k)

void insert(pii a)\le b_\),使得 \(\sum\limits _^ |a_i-b_i|\) 最小 .

solution

關於暴力與轉移方程

首先對於暴力轉移,定義\(dp_\)為轉移到i點,權值為j的最小花費.

那麼有轉移方程 $dp_= min(dp_)+|a_i-j| $ [k>=j]

函式影象及證明

然後分析\(dp_\) 構成的函式,定義 \(f(x)=dp_i\) ,那麼可以得到\(f(x)\)是乙個下凸函式 [斜率單調不遞減]

首先對於i=1的情況,影象是:

很顯然是乙個下凸函式

其中y表示花費,x表示b1的取值,a1表示原來第乙個點的值

再觀察上面給出的轉移方程,發現對於乙個j,用到的是大於等於自己的k對應的最小值

所以那段下降的函式是無用的

然後加入a2,但考慮a2構成的影象是和上面a1的影象相同的,然後再與修改後的轉移影象相疊加,不難發現影象的斜率單調不減

所要維護的東西

由上面的推導可知:

加入乙個數之後,影象會有所改變,並且我們不用管斜率小於等於0的部分函式

所以就始終維護乙個斜率大於0且單調遞增函式即可,並且答案就為那個下凹點

假設我們考慮 \(a_i\) ,那麼 \(x< a_i\)的部分的斜率都要 -1,\(x> a_i\)的部分斜率都要 +1

如何維護斜率

加入第乙個點後 \(f(x)\) 是乙個斜率為1的遞增函式

那麼就放入\(a_1\),表示 \([a_1,\infty]\) 的區域性函式斜率都為1

如果加入乙個 \(a_2\)

若\(a_2\ge a_1\) ,那麼 \([a_1,a_2]\) 的區域性函式斜率變為0 ,\([a_2,\infty]\) 斜率變為2

若\(a_2,那麼 \([a_2,a_1]\) 的區域性函式斜率變成1,\([a_1,\infty]\) 的斜率變為2

對於第一種情況,可以看做 \([a1,\infty]\) $\to $ \([a1,a2],[a2,a2],[a2,\infty]\) 分別對應 0,1,2三種斜率

對於第二種情況,可以看做 \([a1,\infty]\) $\to $ \([a2,a1],[a1,\infty]\) 分別對應 1,2兩種斜率

已知斜率小於等於0的函式部分是不要的

所以對於第一種情況,應該把\(a_1\)這個點給刪掉,並加入兩個\(a_2\)

而第二種情況,只需加入\(a_2\)即可

發現每次只需要呼叫最左邊的點[即最小值],所以用堆維護即可

如何計算答案

發現對於上述第一種情況,整個函式的下凹點改變了,假設原來凹點為\(f(a_1)=y_1\) ,\(,f(a_2)=y_1+k\times (a_2-a_1),k=1\) ,那麼斜率改變後, 凹點位置轉移到\(a_2\),對應 \(f(a_2)\)不變,所以答案增大\(a_2-a_1\)

rt

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

斜率優化DP

斜率優化主要解決的是轉移方程中存在乙個同時與i和j有關的部分時的優化問題 dp i min dp j a i b j 0 include using namespace std typedef long long ll const int maxn 1e5 5 ll a maxn b maxn dp...