【動態規劃】滾動數組的求解(C++)

  雖然接觸動態規劃算法已經有一段時間,給一個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)。

以上爲我的對滾動數組的求解過程的理解,每一句代碼均通過本人測試可用,若有問題,但願你們提出斧正。

 

 

 

尊重知識產權,轉載引用請通知做者並註明出處!

相關文章
相關標籤/搜索