生活随笔
收集整理的這篇文章主要介紹了
记忆化搜索的研究
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
記憶化搜索:
?
算法上依然是搜索的流程,但是搜索到的一些解用動態規劃的那種思想和模式作一些保存。
一般說來,動態規劃總要遍歷所有的狀態,而搜索可以排除一些無效狀態。
更重要的是搜索還可以剪枝,可能剪去大量不必要的狀態,因此在空間開銷上往往比動態規劃要低很多。
記憶化算法在求解的時候還是按著自頂向下的順序,但是每求解一個狀態,就將它的解保存下來,以后再次遇到這個狀態的時候,就不必重新求解了。這種方法綜合了搜索和動態規劃兩方面的優點,因而還是很有實用價值的。上傳/更換附件動態規劃的另一種實現形式——記憶化搜索的應用。
?
動態規劃和記憶化搜索:
?
動態規劃:就是一個最優化問題,先將問題分解為子問題,并且對于這些分解的子問題自身就是最優的才能在這個基礎上得出我們要解決的問題的最優方案,要不然的話就能找到一個更優的解來替代這個解,得出新的最優自問題,這當然是和前提是矛盾的。動態規劃不同于 貪心算法,因為貪心算法是從局部最優來解決問題,而動態規劃是全局最優的。用動態規劃的時候不可能在子問題還沒有得到最優解的情況下就做出決策,而是必須等待子問題得到了最優解之后才對當下的情況做出決策,所以往往動態規劃都可以用 一個或多個遞歸式來描述。而貪心算法卻是先做出一個決策,然后在去解決子問題。這就是貪心和動態規劃的不同。
一般遇到一個動態規劃類型的問題,都先要確定最優子結構,還有重疊子問題,這兩個是動態規劃最大的特征,然后就是要寫 動態規劃的?狀態方程,這個步驟十分十分的重要的,寫動歸方程是需要一定的經驗的,這可以通過訓練來達到目的。接著就是要自底向上的求解問題的,先將最小規模的子問題的最優解求出,一般都用一張表來記錄下求得的解,到后來遇到同樣的子問題的時候就可以直接查表得到答案,最后就是通過一步一步的迭代得出最后問題的答案了。
我的理解最重要的東西就是一定會要一個數組或者其他的存儲結構存儲得到的子問題的解。這樣就可以省很多時間,也就是典型的空間換時間
動態規劃的一種變形就是記憶化搜索,就是根據動歸方程寫出遞歸式,然后在函數的開頭直接返回以前計算過的結果,當然這樣做也需要一個存儲結構記下前面計算過的結果,所以又稱為記憶化搜索。
?
記憶化搜索/遞歸式動態規劃:
?
1.記憶化搜索的思想
????記憶化搜索的思想是,在搜索過程中,會有很多重復計算,如果我們能記錄一些狀態的答案,就可以減少重復搜索量
2、記憶化搜索的適用范圍
????根據記憶化搜索的思想,它是解決重復計算,而不是重復生成,也就是說,這些搜索必須是在搜索擴展路徑的過程中分步計算的題目,也就是“搜索答案與路徑相關”的題目,而不能是搜索一個路徑之后才能進行計算的題目,必須要分步計算,并且搜索過程中,一個搜索結果必須可以建立在同類型問題的結果上,也就是類似于動態規劃解決的那種。
也就是說,他的問題表達,不是單純生成一個走步方案,而是生成一個走步方案的代價等,而且每走一步,在搜索樹/圖中生成一個新狀態,都可以精確計算出到此為止的費用,也就是,可以分步計算,這樣才可以套用已經得到的答案
3、記憶化搜索的核心實現
?????a.?首先,要通過一個表記錄已經存儲下的搜索結果,一般用哈希表實現
?????b.狀態表示,由于是要用哈希表實現,所以狀態最好可以用數字表示,常用的方法是把一個狀態連寫成一個p進制數字,然后把這個數字對應的十進制數字作為狀態
????c.在每一狀態搜索的開始,高效的使用哈希表搜索這個狀態是否出現過,如果已經做過,直接調用答案,回溯
????d.如果沒有,則按正常方法搜索
4、記憶化搜索是類似于動態規劃的,不同的是,它是倒做的“遞歸式動態規劃”。
?
?
?下面POJ 1691 paint a borad的題目:
?
先用普通的深度優先搜索來解答。
在這道題中需要注意的是:
1、如何記錄某個矩形的信息,這里包括了坐標、顏色、是否涂色、上方矩形的個數、下方有哪幾個矩形。
這些都是從便于程序設計的角度抽象出來的。
2、為了得到最少使用刷子的次數,涂色策略應該為:把當前可以涂色的矩形全部涂色。
3、還需要注意如何通過坐標判斷:緊鄰的處于上方的矩形
?
[cpp]?view plaincopy
? ? ? ? ? ?? ?? #include<iostream>?? ?? #include<cstdio>?? #include<cstring>?? #define?MAX_NUM?15??? using?namespace?std;??? ?? typedef?struct?rect{?? ?????? ????int?x1,y1;?? ????int?x2,y2;?? ????int?color;?? ????bool?isPainted;?? ?????????? }Rect;?? ?? ?? Rect?rectArray[MAX_NUM+1];?? ?? int?rectUp[MAX_NUM+1],rectDown[MAX_NUM+1][MAX_NUM+1];?? ?? int?minBrushes,rectNum;?? ?? ?? ?? void?color(int?c){?? ?????? ????for(int?i=1?;i<=?rectNum?;?i++)??{?? ?????????? ????????if(?rectArray[i].color!=c?||?rectArray[i].isPainted?||?rectUp[i]?)??continue?;?? ????????rectArray[i].isPainted?=?true?;?? ????????for(int?j=1?;?j<=?rectNum?;?j++?){?? ?????????????? ????????????if(?rectDown[i][j]?)????rectUp[j]--?;?? ????????}?? ????}?? ?????? }?? ?? ?? ?? void?depthSearch(int?brushes){?? ?????? ????int?i,j,k?;??? ?????? ????if(?brushes?>=?minBrushes?)??return?;?????????????????? ?????? ????for(?i=1?;?i<=rectNum?;?i++){?????????????????????????? ?????????? ????????if(?false?==?rectArray[i].isPainted)?? ????????????break;?? ????}?? ?????? ????if(?i?==?rectNum?+1?){???????????????????????????????? ?????????? ????????minBrushes?=?brushes?;?? ????????return?;?? ????}?? ?????? ?????? ????bool?tmp_isPainted[MAX_NUM+1];?? ????int?tmp_rectUp[MAX_NUM+1],tmp_rectDown[MAX_NUM+1][MAX_NUM+1];?? ?????? ????for(?i=1?;?i<=rectNum?;?i++)?{?? ?????????? ????????if(?rectArray[i].isPainted?||?rectUp[i]?)???continue?;?? ?????????? ?????????? ????????for(?j?=1?;?j<=rectNum?;?j++){?? ?????????????? ????????????tmp_rectUp[j]?=?rectUp[j]?;?? ????????????tmp_isPainted[j]?=?rectArray[j].isPainted?;?? ????????????for(k=1?;?k<=rectNum?;?k++){?? ?????????????????? ????????????????tmp_rectDown[j][k]?=?rectDown[j][k];?? ????????????}?? ????????}?? ?????????? ????????color(?rectArray[i].color?);?? ?????????? ????????depthSearch(?brushes?+1?);?? ?????????? ?????????? ????????for(?j?=1?;?j<=rectNum?;?j++){?? ?????????????? ????????????rectUp[j]?=?tmp_rectUp[j]?;?? ????????????rectArray[j].isPainted??=??tmp_isPainted[j]?;?? ????????????for(k=1?;?k<=rectNum?;?k++){?? ?????????????????? ?????????????????rectDown[j][k]?=?tmp_rectDown[j][k];?? ????????????}?? ????????}?? ?????????? ????}?? ?????????? ?????? }?? ?? ?? int?main(){?? ?????? ?????? ?????? ?????? ????int?testCase;?? ????int?i,j;?? ?????? ????cin>>testCase?;?? ?????? ????for(i=0?;i<testCase?;?i++){?? ?????????? ????????cin>>rectNum?;?? ?????????? ?????????? ????????memset(?rectUp?,?0?,sizeof(rectUp))?;?? ????????memset(?rectDown?,?0?,?sizeof(rectDown)?);?? ?????????? ?????????? ????????for(j=1?;?j<=rectNum?;?j++){?? ?????????????? ????????????cin>>rectArray[j].x1>>rectArray[j].y1>>rectArray[j].x2>>rectArray[j].y2>>rectArray[j].color;?? ????????????rectArray[j].isPainted?=?false?;?? ????????}?? ?????????? ????????for(i=1?;?i<=rectNum?;?i++?){?? ?????????????? ????????????for(?j=1?;?j<=rectNum?;j++){?? ?????????????????? ????????????????if(?i!=j?&&?(?rectArray[j].x2?==?rectArray[i].x1)?&&??? ????????????????????? ?? ????????????????????????!(rectArray[j].y2?<=?rectArray[i].y1?||?rectArray[j].y1?>=?rectArray[i].y2)){?? ?????????????????????????? ????????????????????????rectUp[i]++;?????????????? ????????????????????????rectDown[j][i]=1?;???????? ????????????????????}?? ????????????}?? ????????}?? ?????????? ????????minBrushes?=?MAX_NUM?;?? ????????depthSearch(0);?? ????????cout<<minBrushes<<endl;?? ????}?? ?????? ????return??0;?? }??
?
通過這道題可以看到dfs搜索算法的特點是這樣的:
?
核心的搜索算法一般使用遞歸的形式,而且遞歸函數的參數往往直接和問題的求解對象有一定的關聯。還有就是,在搜索算法函數的開頭一般先放上剪枝條件,這樣主要是起到回溯的作用,否則的話,函數就會無限遞歸了。可以發現對深度搜索進行合適的剪枝可以大大提高搜索的效率。另外,在執行遞歸之前,一般先對當前的局面狀態進行保存,在遞歸函數返回時,在進行恢復。這有點類似于回溯算法的特點。其實dfs函數的參數表示的就是局面的一種狀態。
?
?
下面對于這個問題使用記憶化搜索算法:
總結
以上是生活随笔為你收集整理的记忆化搜索的研究的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。