【动态规划】滚动数组的求解(C++)
虽然接触动态规划算法已经有一段时间,给一个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( == A.size()) return ;
- int f[A.size()];
- f[] = A[];
- if(A.size()>) f[] = max(A[],A[]);
- for(int i =;i<A.size();i++)
- f[i] = A[i] + f[i-]> f[i-]? A[i] +f[i-]:f[i-];
- return f[A.size()-];
- }
这样,在时间复杂度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[].size();
- int dp[row][col];
- int _max = ;
- for(int i =;i<row;i++){
- dp[i][] = matrix[i][];
- dp[i][col-] = matrix[i][col-];
- _max = dp[i][]|dp[i][col-];
- }
- for(int i =;i<col;i++){
- dp[][i] = matrix[][i];
- dp[row-][i] = matrix[row-][i];
- _max = dp[][i]|dp[row-][i];
- }
- for(int i =;i<row;i++)
- for(int j = ;j<col;j++){
- if( == matrix[i][j]) dp[i][j] = min3(dp[i-][j],dp[i][j-],dp[i-][j-]) +;
- 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[].size();
- int dp[][col];
- int _max = ;
- for(int i =;i<col;i++) dp[][i] = matrix[][i];
- for(int i =;i<row;i++)
- for(int j=;j<col;j++){
- if( == j ) dp[][j] = matrix[i][j];
- else{
- if( == matrix[i][j]) dp[][j] = min3(dp[][j],dp[][j-],dp[][j-])+;
- else dp[][j] = ;
- }
- _max = max(_max,dp[][j]);
- dp[][j-] = dp[][j-];
- }
- dp[][col-] = dp[][col-];
- return _max*_max;
- }
这样空间复杂度可以降到O(m)。
以上为个人对滚动数组的求解过程的理解,每一句代码均经过本人测试可用,如有问题,希望大家提出斧正。
尊重知识产权,转载引用请通知作者并注明出处!
【动态规划】滚动数组的求解(C++)的更多相关文章
- 2021.12.10 P2516 [HAOI2010]最长公共子序列(动态规划+滚动数组)
2021.12.10 P2516 [HAOI2010]最长公共子序列(动态规划+滚动数组) https://www.luogu.com.cn/problem/P2516 题意: 给定字符串 \(S\) ...
- 动态规划+滚动数组 -- POJ 1159 Palindrome
给一字符串,问最少加几个字符能够让它成为回文串. 比方 Ab3bd 最少须要两个字符能够成为回文串 dAb3bAd 思路: 动态规划 DP[i][j] 意味着从 i 到 j 这段字符变为回文串最少要几 ...
- POJ_1159 Palindrome (线性动态规划+滚动数组)
题意是说,给定一个字符串,问至少还需要插入多少个字符才能使得该字符串成为回文字符串. 这道题一开始做的时候用了一个简单的动态规划,开了一个5000*5000的数组,用递归形式实现,代码如下: 其中d[ ...
- HDU-1024 Max Sum Plus Plus 动态规划 滚动数组和转移优化
题目链接:https://cn.vjudge.net/problem/HDU-1024 题意 给n, m和一个序列,找m个不重叠子串,使这几个子串内元素和的和最大. n<=1e6 例:1 3 1 ...
- HDOJ-1024(动态规划+滚动数组)
Max Sum Plus Plus HDOJ-1024 动态转移方程:dp[i][j]=max(dp[i][j-1]+a[j],max(dp[i-1][k])+a[j]) (0<k<j) ...
- HDU-10240Max Sum Plus Plus+动态规划+滚动数组
Max Sum Plus Plus 题意:题意理解了老半天,这里是说在给定数列中,取m组子数列,不能有重复,使得这些子序列的和最大: 就比如m=2时候,1 /2/-4/5/6.可以不用拿-4的意思: ...
- nyoj--1184--为了肾六(动态规划+滚动数组)
为了肾六 时间限制:4000 ms | 内存限制:210535 KB 难度:2 描述 最近肾六很流行,goshawk看身边的朋友都用上了apple.自己还用着W年前的Samsung.于是决定去IT ...
- POJ-1038 Bugs Integrated, Inc. (状压+滚动数组+深搜 的动态规划)
本题的题眼很明显,N (1 <= N <= 150), M (1 <= M <= 10),摆明了是想让你用状态压缩dp. 整个思路如下:由于要填2*3或者3*2的芯片,那么就要 ...
- 动态规划(入门,滚动数组,记录的都是状态):SWUSTACM-1010 魔兽争霸之最后的反击
题目: 1010: 魔兽争霸之最后的反击 Time Li ...
随机推荐
- javax.servlet.JspTagException:Illegal use of <when>-style tag without <choose >as its di
1.错误描述 javax.servlet.JspTagException:Illegal use of <when>-style tag without <choose >as ...
- JavaScript的几种常见的创建方式
1.通过Object构造函数或者对象字面量创建单个对象 使用字面量方法创建对象:var stut = {name: "张三"}; 使用内置构造函数创建对象:var stu = ne ...
- 一道bfs与邻接表应用题
Problem Description 终于有一天,王元姬用他的劫打上了最强王者.他号称,他从来不会在偶数段位停留,因为他的实力太强会跳段(这个13我给满分).傲娇棠和翔妹觉得他的13装的都比勇哥哥好 ...
- Alice and Bob HDU - 4268
Alice and Bob's game never ends. Today, they introduce a new game. In this game, both of them have N ...
- freemarker字符串转换成日期和时间(十三)
freemarker字符串转换成日期和时间 1.日期时间转换总结 (1)date用来转换为日期 (2)time用来转换为时间 (3)datetime用来转换为日期和时间 2.展示示例 <#--字 ...
- JavaScript设计模式(10)-观察者模式
观察者模式 1. 介绍 发布者与订阅者是多对多的方式 通过推与拉获取数据:发布者推送到订阅者或订阅者到发布者那边拉 使并行开发的多个实现能彼此独立地进行修改 其实我们在前端开发中使用到的时间监听就是浏 ...
- RobotFramework自动化测试框架的基础关键字(三)
1.1.1 如何定义一个字典 此处我们说的字典,其实就等同于python语言中的字典,和列表一样,字典也是python语言中非常常用的一种数据结构,也类似于Java 语言中的Map. 在 ...
- [BZOJ1002] [FJOI2007] 轮状病毒 (数学)
Description 给定n(N<=100),编程计算有多少个不同的n轮状病毒. Input 第一行有1个正整数n. Output 将编程计算出的不同的n轮状病毒数输出 Sample Inpu ...
- 【bzoj2151】种树
Time Limit: 1000ms Memory Limit: 128MB Description A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府 ...
- highCharts实现简单柱形图
js: function chart(data,title){ $('#container').highcharts({ chart: { type: 'bar' }, title: { text: ...