雖然接觸動態規劃算法已經有一段時間,給一個01揹包問題,可以作到一個表格簡單粗暴下去,而後求得結果,但內心總以爲對這個算法理解十分不到位,抱着對算法的熱愛,網上不少大牛的算法思惟實在讓我佩服的五體投地。在此講一講動態規劃中滾動數組的求解方法,算是對這個知識點作一個記錄,也但願有寫的不妥的地方,你們能不吝賜教。算法
首先,咱們先看看「滾動數組」的例題,你們能夠參考http://www.lintcode.com/en/problem/house-robber/數組
題意大概就是說:一個盜賊要去偷盜一系列房子的財物,房子之間有報警器,當且僅當兩個相鄰的房子同時被偷盜的時候會自動報警,每一個房子有必定的價值量,請問盜賊怎麼作才能偷到最大價值的量。測試
求解這類題目要注意如下四點要素(也能夠說是動態規劃題目的四點要素):spa
一、狀態(State)code
用於存儲小規模問題的結果blog
二、方程(Function)it
由狀態推出的一個方程,即怎麼經過小的狀態來推出大的狀態io
三、初始化(Initialization)class
最初的狀態,做爲方程的起點遍歷
四、結果(Result)
根據每步的狀態求出的結果
回到House-Robber這個題目,這個題目是個典型的滾動數組類型題。給定一個數組,咱們不可以取相鄰的兩個元素,怎麼取法可使取得的結果和最大。好比給定[4,9,1],咱們能夠取[4,1]或者取[9],很明顯一個結果是5,一個結果是9,因此咱們這裏的結果應該是後者的取法。在狀態的確立上,咱們能夠這麼理解,用A[n]數組表示每一個元素(下標從0開始),當咱們遇到第 i 個元素的時候,咱們是取不取,當咱們取了第i-1個元素的時候,咱們就不能取了,要不就去掉第i-1個元素,以便於取得第i個元素。若是咱們用f[n]來表明當遇到第n個元素,咱們作出行動(取仍是不取)以後,可以得到的最大值,那當取到第i個元素的時候,咱們就是判斷f[i-2] + A[i]和f[i-1]的大小,而後f[i] = max(f[i-2]+A[i],f[i-1]),你說是否是呢?咱們無須去管i-3以前的元素是怎麼取的,由於按照這個規律來,在f[i-2]已經包含了到i-2個元素位置可以取得的最大量了。
至此,以上已經闡明瞭這個思路的【狀態】還有【方程】,剩下【初始化狀態】和【結果】,咱們能夠注意到肯定第i個方程的結果,往前須要用到i-2的元素,因此第0個元素和第1一個元素須要做爲初始化的內容,由於這兩個元素均不能用i-2的元素推倒出來,因此有f[0] = A[0],f[1] = max(A[1],A[0])。而結果就是咱們對第n-1個元素作出行動以後的結果,該結果存儲在f[n-1]當中。
經過代碼實現:
long long houseRobber(vector<int> A){ if(0 == A.size()) return 0; int f[A.size()]; f[0] = A[0]; if(A.size()>1) f[1] = max(A[0],A[1]); for(int i =2;i<A.size();i++) f[i] = A[i] + f[i-2]> f[i-1]? A[i] +f[i-2]:f[i-1]; return f[A.size()-1]; }
這樣,在時間複雜度O(n)能夠求得結果,空間複雜度爲O(n)。
【在這裏我稍微拓展一下,f[i]的結果是經過對比f[i-2]+A[i]和f[i-1]的結果得來的,那其實i-3及其之前的空間都不須要再用到,因此咱們能夠只用三個空間重複使用來表示每個狀態,以達到空間複雜度爲O(1)】
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
以上就是對一位數組的滾動數組算法求解過程,接下來探討一下二維數組的滾動數組求解過程,你們能夠參考http://www.lintcode.com/en/problem/maximal-square/
題意:給定一個只包含01的二維數組,求出最大的正方形,使這個正方形的全部元素都爲1
咱們用Matrix[n][m]表示這個二維數組,dp[n][m]表示左上角最大符合條件的正方形邊長。首先,四個要素咱們先來肯定一下。【狀態】咱們要肯定第[i,j]的狀態要怎麼肯定呢,往前推一步有三個狀態,分別是[i-1,j],[i,j-1]和[i-1,j-1],這三個狀態一樣表示了左上角最大的正方形,咱們經過圖來表示理解一下:
這裏爲了方便畫出了dp[i-1][j],dp[i][j-1],dp[i-1][j-1]相等的狀況,能夠很容易看出,當dp[i-1][j],dp[i][j-1],dp[i-1][j-1]相等的時候dp[i][j]等於dp[i-1][j],dp[i][j-1],dp[i-1][j-1]其中的一個加上1,若是dp[i-1][j],dp[i][j-1],dp[i-1][j-1]不相等,那dp[i][j]取決於dp[i-1][j],dp[i][j-1],dp[i-1][j-1]之中小的那一個,這裏偷懶就不畫圖了,你們能夠本身動手畫畫看,嘿嘿~。所以【狀態】肯定了,【方程】也就容易獲得dp[i][j] = min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1。【初始化狀態】能夠想到從[1,1]開始才能應用這個方程(假定咱們是從[0,0]向[n,m]逐漸肯定狀態),因此i=0和j=0都是須要初始化的內容,這種狀況下dp[i][j] = Matrix[i][j]。【結果】爲dp矩陣中最大值的平方max{dp[n][m]}2。
代碼實現:
int maxSquare(vector<vector<int> > &matrix) { // write your code here int row = matrix.size(); int col = matrix[0].size(); int dp[row][col]; int _max = 0; for(int i =0;i<row;i++){ dp[i][0] = matrix[i][0]; dp[i][col-1] = matrix[i][col-1]; _max = dp[i][0]|dp[i][col-1]; } for(int i =0;i<col;i++){ dp[0][i] = matrix[0][i]; dp[row-1][i] = matrix[row-1][i]; _max = dp[0][i]|dp[row-1][i]; } for(int i =1;i<row;i++) for(int j = 1;j<col;j++){ if(1 == matrix[i][j]) dp[i][j] = min3(dp[i-1][j],dp[i][j-1],dp[i-1][j-1]) +1; else dp[i][j] = matrix[i][j]; _max = max(dp[i][j],_max); } return _max*_max; }
時間複雜度爲O(n*m),空間複雜度爲O(n*m)
【拓展:參考一位數組的拓展思路,咱們肯定[i,j]個狀態,分別用到[i-1,j],[i,j-1],[i-1,j-1]這三個狀態,因此咱們能夠想到能夠用4個空間來重複表示各個狀態,但因爲對於二維數組,當換行的時候到了行首會丟失這個狀態(由於4個空間在表示上一行末尾的四個狀態了),因此咱們只能用兩行(2*n)個空間來維持這個狀態】下面給出拓展後的代碼,你們僅供參考,由於初始化狀態被寫在遍歷過程當中,可能會形成混亂,請諒解:
int maxSquare(vector<vector<int> > &matrix) { int row = matrix.size(); int col = matrix[0].size(); int dp[2][col]; int _max = 0; for(int i =0;i<col;i++) dp[0][i] = matrix[0][i]; for(int i =1;i<row;i++) for(int j=0;j<col;j++){ if( 0 == j ) dp[1][j] = matrix[i][j]; else{ if(1 == matrix[i][j]) dp[1][j] = min3(dp[0][j],dp[1][j-1],dp[0][j-1])+1; else dp[1][j] = 0; } _max = max(_max,dp[1][j]); dp[0][j-1] = dp[1][j-1]; } dp[0][col-1] = dp[1][col-1]; return _max*_max; }
這樣空間複雜度能夠降到O(m)。
以上爲我的對滾動數組的求解過程的理解,每一句代碼均通過本人測試可用,若有問題,但願你們提出斧正。
尊重知識產權,轉載引用請通知做者並註明出處!