本篇部落格主要講的是斜率優化一些最基礎的定義以及做法,高階版可以見 這篇部落格
在某些情況下,\(dp\) 的時間複雜度仍然超出了題目的限制,這時我們就要考慮對其進行優化
斜率優化是對決策進行優化的一種方法
它適用於類似 \(f[i]=min/max(a[i]×b[j]+c[i]+d[j])\) 的方程
斜率優化由乙個單調佇列進行維護,維護滿足要求的最優下標,在更新當前的 \(f\) 也就是 \(dp\) 陣列時,直接取隊首即可。
接下來以幾道簡單的例題講解
題目鏈結
題意很簡單,讓我們任意分組,使得分出來組的那一段 \(sum\) 在進行那個式子的運算之後最大,於是我們可以推轉移方程。
定義 \(f[i][j]\) 表示前 \(i\) 個人,分成 \(j\) 組的最大值。
容易得到 \(f[i][j] = \max_(f[i][j],f[k][j-1] + a\times (sum[i]-sum[k])^2+b\times (sum[i]-sum[k]) + c)\)
我們可以發現每次的最大值都是從之前轉移來,並且第二維是沒有意義的,所以可以轉化為一維。
要使當前被最優地更新,我們可以得到如下不等式:
\(f_+a\times (sum_i-sum_)^2+b\times (sum_i-sum_) + c > f_+a\times (sum_i-sum_)^2+b\times (sum_i-sum_) + c\)
然後移項使不等號一邊只剩下常數,得到:
\[\frac-f_+a\times sum_^2-a\times sum_^2+b\times sum_-b\times sum_}-sum_} > 2\times a\times sum_i
\]於是我們可以得到斜率為 \(2\times a\times sum_i\)。
由於 \(a<0\) ,斜率單調減,那麼維護乙個上凸包即可。
#includeusing namespace std;
#define int long long
#define gc() (p1 == p2 ? (p2 = buf + fread(p1 = buf, 1, 1 << 20, stdin), p1 == p2 ? eof : *p1++) : *p1++)
#define read() ( while(c >= '0' && c <= '9') x = x * 10 + (c & 15), c = gc(); f * x; })
char buf[1 << 20], *p1, *p2;
#define db double
const int maxn = 1e6+10;
int x[maxn];
int f[maxn];
int sum[maxn];
int q[maxn];
int a,b,c;
inline db calc(int k1,int k2)
signed main()
int head = 1,tail = 1;
for(int i = 1;i <= n;++i)
printf("%lld\n",f[n]);
return 0;
}
有 \(n\) 名學生參加軍訓,軍訓的一大重要內容就是走佇列,而乙個佇列的不規整程度是該隊中最高的學生的身高與最矮的學生的身高差值的平方。現在要將 \(n\) 名參加軍訓的學生重新分成 \(k\) 個佇列,每個佇列的人數不限,請求出所有佇列的不規整程度之和的最小值。
第一行兩個整數 \(n,k\) ,表示學生人數和佇列數。
第二行 \(n\) 個實數,表示每名學生的身高。身高範圍在 \(140∼200cm\) 之間,保留兩位小數。
乙個實數表示答案,保留 \(2\) 位小數。
樣例輸入1:
3 2樣例輸出1:170.00 180.00 168.00
4.00樣例輸入2:
5 2樣例輸出2:170.00 180.00 168.00 140.59 199.99
1023.36資料點
\(n\)
\(k\)
\(1-2\)
\(\leqslant 10\)
\(\leqslant 5\)
\(3-6\)
\(\leqslant 100\)
\(\leqslant 10\)
\(7,8\)
\(\leqslant 10^5\)
\(1\)
\(9,10\)
\(\leqslant 10^5\)
\(2\)
\(11,12\)
\(\leqslant 10^5\)
\(3\)
\(13,14\)
\(\leqslant 10^5\)
\(4\)
\(15-20\)
\(\leqslant 10^5\)
\(\leqslant 20\)
首先最簡單的方法就是離散化一下,直接暴力 \(dp\) ,顯然是可以過的。
但是我們作為新時代的青年當然要搞優秀的演算法,所以考慮把暴力 \(dp\) 改成斜率優化。
與上乙個題類似,狀態轉移方程仍然是二維的,轉移也很容易寫出來,即:
\[f[i][j] = \min_(f[i][j],f[k][j-1] + (a[k+1] - a[i]) * (a[k+1] - a[i]))
\]這裡我們需要保留第二維 \(j\) ,具體沒有什麼大影響,只需要在計算斜率的時候多傳乙個參就行了。
然後我們推不等式,容易得到:
\[\frac < 2 \times a[i]
\]然後跟上乙個題一樣單調佇列維護斜率即可。由於 \(x\) 是單調的,所以我們只需要看斜率,維護乙個下凸包即可。
#include#include#include#include#define db double
#define rint register int
#define max(a,b) (a > b ? a : b)
#define min(a,b) (a < b ? a : b)
const int maxn = 1e5+10;
int n,k;
db a[maxn];
db f[maxn][25];
int q[maxn],head,tail;
inline db calc(int k1,int k2,int j)
int main()
std::sort(a+1,a+n+1);
rint tot = std::unique(a+1,a+n+1) - a - 1;
head = 1,tail = 0;
for(rint i = 1;i <= tot;++i)f[i][1] = (a[1] - a[i]) * (a[1] - a[i]);
for(rint j = 2;j <= k;++j)
} printf("%.2lf\n",f[tot][k]);
return 0;
}
題目鏈結
一般的套路題,分組的暴力 \(dp\) 應該非常好想,這裡不再一點一點推,然後我們會發現,每次分組是把當前組的兩部分乘起來,然後就瞬間沒了思路。
由於乙個乙個區間進行處理不現實,所以一定有什麼結論,然後開始推:
假設有 \(4\) 個數 \(a,b,c,d\) 組成乙個序列,給出乙個固定的分組 \(,,\) 。
我們會發現分組的先後順序對答案沒有影響(建議自己多舉幾個例子),然後我們就可以跟一般的分組 \(dp\) 一樣轉移了。
容易得出狀態轉移:
\[f[i][j] = f[k][j-1] + sum[k] \times (sum[i] - sum[k])
\]然後我們又開始寫斜率不等式:
\[\frac < sum[i]
\]維護下凸包。
#includeusing namespace std;
#define int long long
#define gc() (p1 == p2 ? (p2 = buf + fread(p1 = buf, 1, 1 << 20, stdin), p1 == p2 ? eof : *p1++) : *p1++)
#define read() ( while(c >= '0' && c <= '9') x = x * 10 + (c & 15), c = gc(); f * x; })
char buf[1 << 20], *p1, *p2;
const int maxn = 1e5+10;
#define db double
int n,k;
int f[maxn][210],pre[maxn][210];
int sum[maxn];
int q[maxn];
inline db calc(int k1,int k2,int j)
inline void print(int x,int gs)
signed main()
} printf("%lld\n",f[n][k]);
int nn = n;
for(int i = k;i >= 1;--i)
return 0;
}
高階一些的斜率優化 \(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 如果把右邊...
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...