【動態規劃】5 從一次函數出發推導斜率優化dp

背景

基于例題《任務安排》逐步推導進行斜率優化。

引入

例題:P2365 任務安排

考慮動態規劃。使用 d p i , j dp_{i,j} dpi,j? 表示前 i i i 個任務分了 j j j 段的最小費用。

顯然,有 d p i , j = min ? k = 1 i ? 1 ( d p i , j , d p k , j ? 1 + ( t o t i ? t o t k ) ) ? ( s u m [ i ] + s ? j ) ) dp_{i,j} = \min_{k=1}^{i-1} (dp_{i,j},dp_{k,j-1} + (tot_i-tot_k))*(sum[i]+s*j)) dpi,j?=mink=1i?1?(dpi,j?,dpk,j?1?+(toti??totk?))?(sum[i]+s?j))? 。

  • s u m i sum_i sumi? 表示 c i c_i ci? 的前綴和。
  • t o t i tot_i toti? 表示 t i t_i ti? 的前綴和。

前綴和優化后,時間復雜度 O ( n 3 ) O(n^3) O(n3),得到 60pts.

代碼

#include <bits/stdc++.h>
using namespace std;
int n,s,ans,t[5005],c[5005],dp[5005][5005],sum[5005],tot[5005];
int main()
{cin >> n >> s;for (int i=1;i<=n;i++){cin >> t[i] >> c[i];sum[i] = sum[i-1] + t[i];tot[i] = tot[i-1] + c[i];}memset(dp,0x3f,sizeof(dp));ans = 0x3f3f3f3f;dp[0][0] = 0;for (int i=1;i<=n;i++){for (int j=1;j<=i;j++){for (int k=0;k<i;k++){dp[i][j] = min(dp[i][j],dp[k][j-1] + (tot[i]-tot[k])*(sum[i]+s*j));}	}	}for (int i=1;i<=n;i++){ans = min(ans,dp[n][i]);}cout<<ans;return 0;
}

如何進一步優化呢?

我們發現,可以把有關 s s s 的計算在前面完成。也就是 費用提前計算 ,就不需要枚舉分的段數了。

得到狀態轉移方程 d p i = min ? ( d p i , d p j + s u m i ? t o t i ? s u m i ? t o t j + t o t n ? s ? t o t j ? s ) dp_i = \min(dp_i,dp_j + sum_i*tot_i-sum_i*tot_j + tot_n*s-tot_j*s) dpi?=min(dpi?,dpj?+sumi??toti??sumi??totj?+totn??s?totj??s)

代碼

#include <bits/stdc++.h>
using namespace std;
long long n,s,ans,t[5005],c[5005],dp[5005],sum[5005],tot[5005];
int main()
{cin >> n >> s;for (int i=1;i<=n;i++){cin >> t[i] >> c[i];sum[i] = sum[i-1] + t[i];tot[i] = tot[i-1] + c[i];dp[i] = 1e18;}ans = 1e18;dp[0] = 0;for (int i=1;i<=n;i++){for (int j=0;j<i;j++){dp[i] = min(dp[i],dp[j] + sum[i]*(tot[i]-tot[j]) + (tot[n]-tot[j])*s);}	}cout<<dp[n];return 0;
}

正文

狀態轉移方程 d p i = min ? ( d p i , d p j + s u m i ? t o t i ? s u m i ? t o t j + t o t n ? s ? t o t j ? s ) dp_i = \min(dp_i,dp_j + sum_i*tot_i-sum_i*tot_j + tot_n*s-tot_j*s) dpi?=min(dpi?,dpj?+sumi??toti??sumi??totj?+totn??s?totj??s)

把與 i , j i,j i,j 有關的各單獨放在一起,得到 d p i = min ? ( d p i , d p j + s u m i ? t o t i + t o t n ? s ? t o t j ? ( s u m i + s ) ) dp_i = \min(dp_i,dp_j + sum_i*tot_i + tot_n*s - tot_j*(sum_i+s)) dpi?=min(dpi?,dpj?+sumi??toti?+totn??s?totj??(sumi?+s))

去掉最小值,得到 d p i = d p j + s u m i ? t o t i + t o t n ? s ? t o t j ? ( s u m i + s ) dp_i = dp_j + sum_i*tot_i + tot_n*s - tot_j*(sum_i+s) dpi?=dpj?+sumi??toti?+totn??s?totj??(sumi?+s)

移項,得到 d p j = t o t j ? ( s u m i + s ) + d p i ? s u m i ? t o t i ? t o t n ? s dp_j = tot_j*(sum_i+s) + dp_i - sum_i*tot_i - tot_n*s dpj?=totj??(sumi?+s)+dpi??sumi??toti??totn??s

t o t j tot_j totj? 為橫坐標, d p j dp_j dpj? 為縱坐標的平面直角坐標系中,

這是一條 y = ( s + s u m i ) ? x + d p i ? s u m i ? t o t i ? t o t n ? s y = (s+sum_i) * x + dp_i - sum_i * tot_i - tot_n * s y=(s+sumi?)?x+dpi??sumi??toti??totn??s 的直線。

寫成 y = k x + b y = kx+b y=kx+b 的形式, k = s + s u m i k = s+sum_i k=s+sumi? b = d p i ? s u m i ? t o t i ? t o t n ? s b = dp_i-sum_i*tot_i-tot_n*s b=dpi??sumi??toti??totn??s.

由于 k k k 是定值,所求的 d p i dp_i dpi? 存在于 b b b 中,所以我們只需要找到最小的 b b b 即可。


如何尋找最小的 b b b

發現有一些點會出現在這條直線上,我們把這樣的點稱為 決策點,即 ( t o t j , d p j ) (tot_j,dp_j) (totj?,dpj?)

對于這些決策點,由于 k k k 是定值,所以有且只有一條 k = s + s u m i k=s+sum_i k=s+sumi? 的直線經過一個決策點,這些決策點一共會產生不超過 j j j 條直線。

對于已知的一個決策點 ( t o t j , d p j ) (tot_j,dp_j) (totj?,dpj?),我們把它們帶入到一次函數表達式里去,就能解出一個 b b b ,枚舉 j j j 得到最小的 b b b 即可。

但這種方法過于樸素,時間復雜度不變。考慮優化。

由于我們是從決策點出發,推導 b b b 的值。則說明決策點坐標(或者說 j j j )與 b b b 之間存在線性關系。考慮決策點坐標之間的關系來優化。

對于三個決策點 ( t o t j 1 , d p j 1 ) , ( t o t j 2 , d p j 2 ) , ( t o t j 3 , d p j 3 ) (tot_{j_1},dp_{j_1}),(tot_{j_2},dp_{j_2}),(tot_{j_3},dp_{j_3}) (totj1??,dpj1??),(totj2??,dpj2??),(totj3??,dpj3??) (我們設這三點 j 1 < j 2 < j 3 j_1 < j_2 < j_3 j1?<j2?<j3? ,由于 t , c > 0 t,c > 0 t,c>0 ,所以這三點的橫坐標依次遞增,即 t o t j 1 < t o t j 2 < t o t j 3 tot_{j_1} < tot_{j_2} < tot_{j_3} totj1??<totj2??<totj3??)來說,當這三個決策點有且僅有取 ( t o t j 2 , d p j 2 ) (tot_{j_2},dp_{j_2}) (totj2??,dpj2??) 時, b b b 有最小值,那么這三點所構成的直線不會兩兩重合,并分為兩種情況:

情況 1 ( j 2 j_2 j2? j 1 j_1 j1? j 3 j_3 j3? 的連線上方)

當這三點構成一個向上凸出的形狀,即 上凸 。顯然此時 j 2 j_2 j2? 一定不會使得 b b b 取最小值,如下圖所示。

image.png

情況 2 ( j 2 j_2 j2? j 1 j_1 j1? j 3 j_3 j3? 的連線下方)

當這三點構成一個向下凸出的形狀,即 下凸 。顯然此時 j 2 j_2 j2? 可能使得 b b b 取最小值,如下圖所示。

image.png

發現只有 下凸 的情況 ( j 2 j_2 j2? j 1 j_1 j1? j 3 j_3 j3? 的連線下方) 才可能使 j 2 j_2 j2? 取到最小的 b b b

則有 d p j 2 ? d p j 1 t o t j 2 ? t o t j 1 < d p j 3 ? d p j 2 t o t j 3 ? t o t j 2 \frac{dp_{j_2}-dp_{j_1}}{tot_{j_2}-tot_{j_1}} < \frac{dp_{j_3}-dp_{j_2}}{tot_{j_3}-tot_{j_2}} totj2???totj1??dpj2???dpj1???<totj3???totj2??dpj3???dpj2???

即直線 j 1 → j 2 j_1 \to j_2 j1?j2? k k k 小于 j 2 → j 3 j_2 \to j_3 j2?j3? 直線的 k k k ,本質上是這兩條直線的斜率關系。


因此,我們需要維護 相鄰兩點間直線的 k k k (斜率) ,并當它們 單調遞增 時, j 2 j_2 j2? 所得到的 b b b 就可能是最小值。

那么什么時候 j 2 j_2 j2? 所取的 b b b 就一定是最小值呢?

image.png

我們發現,當一段單調遞增的 k k k 滿足一個點的左邊的 k ’ k’ k 都小于 k k k ,右邊的 k ’ k’ k 都大于 k k k 時,這個點就是使 b b b 最小的點。

如果我們只維護 相鄰兩點間連線斜率大于等于 k k k k ′ k' k (斜率),那么在這個單調遞增的序列中最小值就能使 b b b 最小。

這不就是單調隊列的思路嗎?

總結一下:

  1. 我們用單調隊列維護相鄰兩點間直線的 k k k ,使其單調遞增。
  2. 在單調隊列里放的是 k k k 單調遞增的點的編號。
  3. 最終答案是單調隊列的隊頭坐標代入 d p i = d p j + s u m i ? t o t i + t o t n ? s ? t o t j ? ( s u m i + s ) dp_i = dp_j + sum_i*tot_i + tot_n*s - tot_j*(sum_i+s) dpi?=dpj?+sumi??toti?+totn??s?totj??(sumi?+s).
  4. 為了維護單調性,我們需要從左側隊頭開始刪除。即判斷隊頭斜率 d p q h e a d + 1 ? d p q h e a d t o t q h e a d + 1 ? t o t q h e a d ≤ s + s u m i \frac{dp_{q_{head+1}}-dp_{q_{head}}}{tot_{q_{head+1}}-tot_{q_{head}}} \leq s+sum_i totqhead+1???totqhead??dpqhead+1???dpqhead???s+sumi? 時,把隊頭出隊即可。 為了避免精度問題,且 t o t tot tot 有單調遞增性,那么我們不妨判斷 d p q h e a d + 1 ? d p q h e a d ≤ ( s + s u m i ) ? ( t o t q h e a d + 1 ? t o t q h e a d ) {dp_{q_{head+1}}-dp_{q_{head}}} \leq (s+sum_i) * ({tot_{q_{head+1}}-tot_{q_{head}}}) dpqhead+1???dpqhead??(s+sumi?)?(totqhead+1???totqhead??).
  5. 添加時,如果 q i q_i qi? 不能與前面的點滿足單調性,那么直接把前面的點不斷出隊,直到滿足單調性為止。即當 d p i ? d p q t a i l t o t i ? t o t q t a i l ≤ d p q t a i l ? d p q t a i l ? 1 t o t q t a i l ? t o t q t a i l ? 1 \frac{dp_{i}-dp_{q_{tail}}}{tot_{i}-tot_{q_{tail}}} \leq \frac{dp_{q_{tail}}-dp_{q_{tail-1}}}{tot_{q_{tail}}-tot_{q_{tail-1}}} toti??totqtail??dpi??dpqtail???totqtail???totqtail?1??dpqtail???dpqtail?1??? 時不斷出隊即可。同樣避免精度問題,判斷 ( d p i ? d p q t a i l ) ? ( t o t q t a i l ? t o t q t a i l ? 1 ) ≤ ( d p q t a i l ? d p q t a i l ? 1 ) ? ( t o t i ? t o t q t a i l ) ({dp_{i}-dp_{q_{tail}}}) * ({tot_{q_{tail}}-tot_{q_{tail-1}}}) \leq ({dp_{q_{tail}}-dp_{q_{tail-1}}})*({tot_{i}-tot_{q_{tail}}}) (dpi??dpqtail??)?(totqtail???totqtail?1??)(dpqtail???dpqtail?1??)?(toti??totqtail??) 即可。

時間復雜度 O ( n ) O(n) O(n) .

#include <bits/stdc++.h>
using namespace std;
const int N = 300005;
long long n,s,ans,t[N],c[N],dp[N],sum[N],tot[N];
long long q[N],head=1,tail=1;
int main()
{cin >> n >> s;for (int i=1;i<=n;i++){cin >> t[i] >> c[i];sum[i] = sum[i-1] + t[i];tot[i] = tot[i-1] + c[i];dp[i] = 1e18;}ans = 1e18;dp[0] = 0;for (int i=1;i<=n;i++){while (head < tail && dp[q[head+1]]-dp[q[head]] <= (s+sum[i])*(tot[q[head+1]]-tot[q[head]])) head++;dp[i] = dp[q[head]] + sum[i]*tot[i] + tot[n]*s - tot[q[head]]*(sum[i]+s);while (head < tail && (dp[i]-dp[q[tail]])*(tot[q[tail]]-tot[q[tail-1]]) <= (dp[q[tail]]-dp[q[tail-1]])*(tot[i]-tot[q[tail]])) tail--;q[++tail] = i;}cout<<dp[n];return 0;
}

為什么單調隊列初始 head = 1,tail = 1 ,而不能寫作 head = 1,tail = 0 ?
考慮到還有 dp[0] = 0 ,要么把 tail 設為 head ,要么把 tail 設為 head-1 再在隊列里加入 dp[0]

后記

斜率優化看起來好像的確比想象中抽象很多,希望對大家理解斜率優化有所幫助!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/81905.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/81905.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/81905.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MySQL中實現大數據量的快速插入

一、SQL語句優化? 1. ?批量插入代替單條插入? ?單條插入會頻繁觸發事務提交和日志寫入&#xff0c;效率極低。?批量插入通過合并多條數據為一條SQL語句&#xff0c;減少網絡傳輸和SQL解析開銷。 -- 低效寫法&#xff1a;逐條插入 INSERT INTO table (col1, col2) VALUE…

C++23中std::span和std::basic_string_view可平凡復制提案解析

文章目錄 一、引言二、相關概念解釋2.1 平凡復制&#xff08;Trivially Copyable&#xff09;2.2 std::span2.3 std::basic_string_view 三、std::span和std::basic_string_view的應用場景3.1 std::span的應用場景3.2 std::basic_string_view的應用場景 四、P2251R1提案對std::…

廣東省省考備考(第十八天5.23)—言語:語句填空題(聽課后強化訓練)

錯題 解析 橫線出現在文段中間&#xff0c;需結合上下文內容進行分析。文段開篇指出逃離北上廣深的話題時而出現&#xff0c;一些人離開大城市回到小城市。隨后通過轉折詞“但”引出橫線內容&#xff0c;且結合橫線后人才傾向于向更發達的地方流動的內容&#xff0c;橫線處應體…

持續更新 ,GPT-4o 風格提示詞案例大全!附使用方式

本文匯集了各類4o風格提示詞的精選案例&#xff0c;從基礎指令到復雜任務&#xff0c;從創意寫作到專業領域&#xff0c;為您提供全方位的參考和靈感。我們將持續更新這份案例集&#xff0c;確保您始終能夠獲取最新、最有效的提示詞技巧。 讓我們一起探索如何通過精心設計的提…

創建型:建造者模式

目錄 1、核心思想 2、實現方式 2.1 模式結構 2.2 工作流程 2.3 實現案例 2.4 變體&#xff1a;鏈式建造者&#xff08;常見于多參數對象&#xff0c;無需指揮者&#xff09; 3、優缺點分析 4、適用場景 1、核心思想 目的&#xff1a;將復雜對象的構建過程與其表示分離…

力扣-長度最小的子數組

1.題目描述 2.題目鏈接 LCR 008. 長度最小的子數組 - 力扣&#xff08;LeetCode&#xff09; 3.題目分析 這道題目我們使用的也是雙指針。我們可以定義兩個指針都指向數組第一個元素&#xff0c;然后使用right指針遍歷原數組&#xff0c;計算left指針到right指針之間的所有元…

JAVA開發工具延長方案

親測穩定的延長方案與避坑指南 真的搞不懂了&#xff0c;說點專業的術語竟然成了 QINQUAN。那就直接點&#xff0c;把這個方案帶給需要的開發者。 延長工具直通車 保姆級教程 延長方案https://mp.weixin.qq.com/s/uajM2Y9Vz6TnolzcLur_bw還是讓大家看看&#xff0c;發什么會被…

SpringAI開發SSE傳輸協議的MCP Server

SpringAI 訪問地址&#xff1a;Spring AI ? Spring AI?是一個面向人工智能工程的應用框架&#xff0c;由Spring團隊推出&#xff0c;旨在將AI能力集成到Java應用中。Spring AI的核心是解決AI集成的根本挑戰&#xff0c;即將企業數據和API與AI模型連接起來?。 MCP…

JAVA動態生成類

在java的加載過程一般都是要預先定義java類,然后通過經過加載->連接->初始化三步。連接過程又可分為三步:驗證->準備->解析。初始化的類是不允許修改。但是在日常的工作中有時候需要動態生成類,那第這種情況怎么辦呢? 可以這么處理: 1、先定義一個空的類,僅…

深入解析Java微服務架構:Spring Boot與Spring Cloud的整合實踐

深入解析Java微服務架構&#xff1a;Spring Boot與Spring Cloud的整合實踐 引言 隨著云計算和分布式系統的快速發展&#xff0c;微服務架構已成為現代軟件開發的主流模式。Java作為企業級應用開發的核心語言&#xff0c;結合Spring Boot和Spring Cloud&#xff0c;為開發者提…

03_基礎篇-NumPy(下):深度學習中的常用操作

03_基礎篇-NumPy&#xff08;下&#xff09;&#xff1a;深度學習中的常用操作 通過上節課的學習&#xff0c;我們已經對NumPy數組有了一定的了解&#xff0c;正所謂實踐出真知&#xff0c;今天我們就以一個圖像分類的項目為例&#xff0c;看看NumPy的在實際項目中都有哪些重要…

時鐘識別項目報告(深度學習、計算機視覺)

深度學習方式 一、模型架構 本模型采用雙任務學習框架&#xff0c;基于經典殘差網絡實現時鐘圖像的小時和分鐘同步識別。 主干網絡 使用預訓練的ResNet18作為特征提取器&#xff0c;移除原分類層&#xff08;fc層&#xff09;&#xff0c;保留全局平均池化后的512維特征向量。…

openai-whisper-asr-webservice接入dify

openai-whisper-asr-webservice提供的asr的api其實并不兼容openai的api&#xff0c;所以在dify中是不能直接添加到語音轉文字的模型中&#xff0c;對比了下兩個api的傳參情況&#xff0c;其實只要改動一處&#xff0c;就能支持&#xff1a; openai兼容的asr調用中formdata中音頻…

解鎖MySQL性能調優:高級SQL技巧實戰指南

高級SQL技巧&#xff1a;解鎖MySQL性能調優的終極指南 開篇 當前&#xff0c;隨著業務系統的復雜化和數據量的爆炸式增長&#xff0c;數據庫性能調優成為了技術人員面臨的核心挑戰之一。尤其是在高并發、大數據量的場景下&#xff0c;SQL 查詢的性能直接影響到整個系統的響應…

JavaScript 性能優化實戰指南

JavaScript 性能優化實戰指南 前言 隨著前端應用復雜度提升&#xff0c;JavaScript 性能瓶頸日益突出。高效的性能優化不僅能提升用戶體驗&#xff0c;還能增強系統穩定性和可維護性。本文系統梳理了 JavaScript 性能優化的核心思路、常見場景和實戰案例&#xff0c;結合代碼…

服務器磁盤按陣列劃分為哪幾類

以下是服務器磁盤陣列&#xff08;RAID&#xff09;的詳細分類及技術解析&#xff0c;基于現行行業標準與實踐應用&#xff1a; 一、主流RAID級別分類 1. ?RAID 0&#xff08;條帶化&#xff09;? ?技術原理?&#xff1a;數據分塊后并行寫入多塊磁盤&#xff0c;無…

鴻蒙 Location Kit(位置服務)

移動終端設備已經深入人們日常生活的方方面面&#xff0c;如查看所在城市的天氣、新聞軼事、出行打車、旅行導航、運動記錄。這些習以為常的活動&#xff0c;都離不開定位用戶終端設備的位置。 Location Kit 使用多種定位技術提供服務&#xff0c;可以準確地確定設備在室外/室…

二叉樹深搜:在算法森林中尋找路徑

專欄&#xff1a;算法的魔法世界 個人主頁&#xff1a;手握風云 目錄 一、搜索算法 二、回溯算法 三、例題講解 3.1. 計算布爾二叉樹的值 3.2. 求根節點到葉節點數字之和 3.3. 二叉樹剪枝 3.4. 驗證二叉搜索樹 3.5. 二叉搜索樹中第 K 小的元素 3.6. 二叉樹的所有路徑 …

企業級AI搜索解決方案:阿里云AI搜索開放平臺

隨著信息技術的飛速發展&#xff0c;搜索引擎作為信息獲取的重要工具&#xff0c;扮演著不可或缺的角色。阿里云 AI 搜索開放平臺以其強大的技術支持和靈活的開放性&#xff0c;持續為用戶提供高效的搜索解決方案。 一、阿里云 AI 搜索開放平臺 一站式的 AI 搜索開放平臺作為…

自動駕駛中的預測控制算法:用 Python 讓無人車更智能

自動駕駛中的預測控制算法:用 Python 讓無人車更智能 自動駕駛技術近年來取得了令人驚嘆的進步,AI 與邊緣計算的結合讓車輛能夠實時感知環境、規劃路徑并執行駕駛決策。其中,預測控制(Model Predictive Control,MPC) 作為一種先進的控制算法,憑借其對未來駕駛行為的優化…