码迷,mamicode.com
首页 > 编程语言 > 详细

动态规划算法之滚动数组的求解(C++)

时间:2017-02-24 11:54:14      阅读:259      评论:0      收藏:0      [点我收藏+]

标签:nbsp   use   时间复杂度   理解   img   不能   alt   价值   目的   

  虽然接触动态规划算法已经有一段时间,给一个01背包问题,能够做到一个表格简单粗暴下去,然后求得结果,但心里总觉得对这个算法理解十分不到位,抱着对算法的热爱,网上很多大牛的算法思维实在让我佩服的五体投地。在此讲一讲动态规划中滚动数组的求解方法,算是对这个知识点做一个记录,也希望有写的不妥的地方,大家能不吝赐教。

  首先,我们先看看“滚动数组”的例题,大家可以参考http://www.lintcode.com/en/problem/house-robber/

  题意大概就是说:一个盗贼要去偷盗一系列房子的财物,房子之间有报警器,当且仅当两个相邻的房子同时被偷盗的时候会自动报警,每个房子有一定的价值量,请问盗贼怎么做才能偷到最大价值的量。

  求解这类题目要注意以下四点要素(也可以说是动态规划题目的四点要素):

  1、状态(State)

    用于存储小规模问题的结果

  2、方程(Function)

    由状态推出的一个方程,即怎么通过小的状态来推出大的状态

  3、初始化(Initialization)

    最初的状态,作为方程的起点

  4、结果(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)。

以上为个人对滚动数组的求解过程的理解,每一句代码均经过本人测试可用,如有问题,希望大家提出斧正。

 

 

 

尊重知识产权,转载引用请通知作者并注明出处!

动态规划算法之滚动数组的求解(C++)

标签:nbsp   use   时间复杂度   理解   img   不能   alt   价值   目的   

原文地址:http://www.cnblogs.com/GNLin0820/p/6434693.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!