動態規劃之記憶化搜尋

2022-01-10 07:22:07 字數 3232 閱讀 4694

以 noip 2005 採藥 為例:

山洞裡有 m 株不同的草藥,採每一株都需要一些時間 \(t_i\) ,每一株也有它自身的價值 \(v_i\) 。我會給你一段時間 t,在這段時間裡,你可以採到一些草藥。讓採到的草藥的總價值最大。

我不會動態規劃,只會搜尋,我就會直接寫乙個粗暴的dfs:

int n, t;

int tcost[103], mget[103];

int ans = 0;

void dfs(int pos, int tleft, int tans)

dfs(pos + 1, tleft, tans);

dfs(pos + 1, tleft - tcost[pos], tans + mget[pos]);

}int main()

這就是個十分智障的大暴搜是吧 ......

emmmmmm....... \(30\) 分

然後我心血來潮,想不借助任何 "外部變數"(就是 dfs 函式外且值隨 dfs 執行而改變的變數), 比如 ans

把 ans 刪了之後就有乙個問題:我們拿什麼來記錄答案?

答案很簡單:

返回值!

此時 \(dfs(pos,tleft)\) 返回在時間 \(tleft\) 內採集\(pos\) 個草藥,能獲得的最大收益

不理解就看看**吧:

int n, time;

int tcost[103], mget[103];

int dfs(int pos, int tleft)

int main()

emmmmmm....... 還是 \(\) 分

但這個時候,我們的程式已經不依賴任何外部變數了。

然後我非常無聊,將所有 dfs 的返回值都記錄下來,竟然發現……

震驚,對於相同的 pos 和 tleft,dfs 的返回值總是相同的!

想一想也不奇怪,因為我們的 dfs 沒有依賴任何外部變數。

旁白:像 \(tcost[103]\) , \(mget[103]\) 這種東西不算是外部變數,因為它們的值在 dfs 過程中不會被改變。

然後?開個陣列 \(mem\) , 記錄下來每個 \(dfs(pos,tleft)\) 的返回值。剛開始把 \(mem\) 中每個值都設成 \(-1\) (代表沒訪問過)。每次剛剛進入乙個 dfs 前(我們的 dfs 是遞迴呼叫的嘛),都檢測 \(mem[pos][tleft]\) 是否為 \(-1\) , 如果是就正常執行並把答案記錄到 \(mem\) 中,否則?

直接返回 \(mem\) 中的值!

int n, t;

int tcost[103], mget[103];

int mem[103][1003];

int dfs(int pos, int tleft)

int main()

此時 \(mem\) 的意義與 dfs 相同:

在時間 \(tleft\) 內採集\(pos\) 個草藥,能獲得的最大收益

這能 ac ?

能。這就是 "採藥" 那題的 ac **

好我們 yy 出了記憶化搜尋

總結一下記憶化搜尋是啥:

有人會問:記憶化搜尋難道不是搜尋?

是搜尋。但個人認為她更像 dp :

不信你看 \(mem\) 的意義:

在時間 \(tleft\) 內採集\(pos\) 個草藥,能獲得的最大收益

這不就是 dp 的狀態?

由上面的**中可以看出:

\(mem[pos][tleft] = max(mem[pos+1][tleft-tcost[pos]]+mget[pos]\ ,\ mem[pos+1][tleft])\)

這不就是 dp 的狀態轉移?

個人認為:

記憶化搜尋約等於動態規劃,(印象中)任何乙個 dp 方程都能轉為記憶化搜尋

大部分記憶化搜尋的狀態/轉移方程與 dp 都一樣,時間複雜度/空間複雜度與不加優化的dp 完全相同

比如:\(dp[i][j][k] = dp[i+1][j+1][k-a[j]] + dp[i+1][j][k]\)

轉為

int dfs(int i, int j, int k) 

int main()

把這道題的 dp 狀態和方程寫出來

根據他們寫出 dfs 函式

新增記憶化陣列

舉例:\(dp_ = max\+1\}\quad 1 \leq j < i and a_(最長上公升子串行)

轉為

int dfs(int i) 

int main()

寫出這道題的暴搜程式(最好是 dfs )

將這個 dfs 改成 "無需外部變數" 的 dfs

新增記憶化陣列

舉例:本文最開始介紹 "什麼是記憶化搜尋" 時舉的 "採藥" 那題的例子

優點:舉例:給你乙個有向圖(注意不是完全圖),經過每條邊都有花費,求從點 1 出發,經過每個點恰好一次後的最小花費(最後不用回到起點),保證路徑存在。

dp 狀態很顯然:

設 \(dp_\) 表示身處在 \(pos\) 處,走過 \(mask\) (mask 為乙個二進位制數)中的頂點後的最小花費

常規 \(dp\) 的狀態數為 \(o(n\cdot 2^n)\) , 轉移複雜度(所有的加在一起)為 \(o(m)\)

但是!如果我們用記憶化搜尋,就可以避免到很多無用的狀態,比如 \(pos\) 為起點卻已經經過了 \(>1\) 個點的情況。

舉例:用常規 dp 寫 "合併石子" 需要先列舉區間長度然後列舉起點,但記憶化搜尋直接列舉斷點(就是列舉當前區間由哪兩個區間合併而成)然後遞迴下去就行

缺點:

int g[maxn];

int f(傳入數值)

int main()

動態規劃之記憶化搜尋

一.動態規劃 動態規劃 dynamic programming 與 分治思想 有些相似,都是利用將問題分 為子問題,並通過合併子問題的解來獲得整個問題的解。於 分治 的不同之處在 於,對於乙個相同的子問題動態規劃演算法不會計算第二次,其實現原理是將每乙個計算過的子問題的值儲存在乙個表中。二.記憶化搜...

動態規劃 記憶化搜尋

記憶化搜尋顧名思義是在搜尋的過程中通過記錄搜尋的中間狀態從而達到減少重複搜尋的方法,通常用在搜尋樹 現重複子節點的情況。例題 滑雪 給定乙個r行c列的矩陣,表示乙個矩形網格滑雪場。矩陣中第 i 行第 j 列的點表示滑雪場的第 i 行第 j 列區域的高度。乙個人從滑雪場中的某個區域內出發,每次可以向上...

動態規劃 記憶化搜尋(滑雪)

給定乙個r行c列的矩陣,表示乙個矩形網格滑雪場。矩陣中第 i 行第 j 列的點表示滑雪場的第 i 行第 j 列區域的高度。乙個人從滑雪場中的某個區域內出發,每次可以向上下左右任意乙個方向滑動乙個單位距離。當然,乙個人能夠滑動到某相鄰區域的前提是該區域的高度低於自己目前所在區域的高度。下面給出乙個矩陣...