动态规划第六讲——leetcode上的动态规划汇总(下)
2014-07-21 15:22
323 查看
接下来的几道题,都是有关路径问题,这可以说是DP问题的一种典型应用。路径有一个维度的;也有两个维度的。
Eg10:Climbing Stairs
这道题目比较简单,重在分析思路。
Eg11:Minimum Path Sum
分析:略
Eg12:Unique Paths
分析:略
Eg13:Unique Paths II
Eg14:Jump Game
分析:这道题目与其说是DP,不如说是GA(贪心算法)。是的,这里又隐含一条我们以后要谈到的规律:谈心算法实际上是DP问题的加强版,每一个GA都可以用DP求解,反之不然。
我们仍然记得,DP问题的递推表达式类似:
dp[i][j]= max{****};如果对max内部包含的时间复杂度是O(n)或者O(i)、O(j)的大小;那么此时如果贪心算法能够使用,往往需要O(1)的算法即可。
分析二:如果使用DP,对本题而言,dp[i] ||= {dp[j]&&(j+A[j]>=i)}
但是,这样毕竟有些浪费,比如对于{1, 2, 3, 4, 5}很明显到i的时候,可以忽略前面部分的i-2部分的计算结果,都可以合并到i-1之中,于是就有了GA:
Eg15:Jump Game II
本题和上题目类似,代码如下:
Eg16:Wildcard Matching
分析,这是一个双序列问题,典型的DP问题。但是,如果我们使用DP来求解,这道题目会超时。我们先来看看DP的解法。
dp[i+1][j+1]:s(0, i), t(0, j)是否能够匹配,res=dp[m]
;
递推关系就不写在这里了,这是一个2个方向的递归问题,需要注意的是时间和空间复杂度都是O(m*n), 因为实际测试的规模m, n在10^5, 所以没法通过。细细思考,有很多计算是不必要的;
计算dp[i+1][j+1], 仅仅需要dp[i][j+1], dp[i+1][j], dp[i][j].另外,如果是s="aaaaaaaaaa" t="aaaaaaaaaaaaaaaaaaa*"那么,我们计算dp[10][j],如果上一行左侧元素全部是0, 本行在0之前的计算都是冗余的。也就是说,我们仅仅需要从上一行不是0的地方开始算起。例如,上面的例子,dp[1][1]=true, dp[1][2]=false之后的dp[1][j]=false
是一直成立的。这样,这个问题就变成了一个回溯问题,见下面的代码。所以,这类求解存在性而非最优化的题目,使用DP往往不是最优的解法。 下面的这个解法,本质上是贪心算法,就是让固定长度的s匹配最长的t;如果失败,那么回溯到上一个*所在的位置。
需要注意的是,在这种模式下,"abc" 与 "a*"是匹配的。
Eg10:Climbing Stairs
这道题目比较简单,重在分析思路。
Eg11:Minimum Path Sum
分析:略
class Solution { public: int minPathSum(vector<vector<int> > &grid) { int row, col; row=grid.size(); if(row==0) return -1; col=grid[0].size(); int i,j; for(j=1;j<col;j++) grid[0][j]+=grid[0][j-1]; for(i=1;i<row;i++){ grid[i][0]+=grid[i-1][0]; } for(i=1;i<row;i++){ for(j=1;j<col;j++){ grid[i][j] += min(grid[i-1][j],grid[i][j-1]); } } return grid[row-1][col-1]; } };
Eg12:Unique Paths
分析:略
class Solution { public: int uniquePaths(int m, int n) { if(m<1 || n<1){ return 0; } int a[m] ; int i,j; fill(a[0], a[0]+n, 1); for(j=1;j<m;j++) a[j][0]=1; for(i=1;i<m;i++) for(j=1;j<n;j++) a[i][j]=a[i-1][j] + a[i][j-1]; return a[m-1][n-1]; } };
Eg13:Unique Paths II
class Solution { public: int uniquePathsWithObstacles(vector<vector<int> > &obstacleGrid) { int m, n; m=obstacleGrid.size(); if(m==0) return 0; n=obstacleGrid[0].size(); if(n==0) return 0; int i,j; for(i=0;i<m;i++) for(j=0;j<n;j++) if(obstacleGrid[i][j]==1) obstacleGrid[i][j]=-1; for(j=0;j<n;j++) if(obstacleGrid[0][j]==0) obstacleGrid[0][j]=1; else if(obstacleGrid[0][j]==-1) break; for (i = 0; i < m; ++i){ if(obstacleGrid[i][0]==0) obstacleGrid[i][0]=1; else if(obstacleGrid[i][0]==-1) break; } for(i=1;i<m;i++){ for(j=1;j<n;j++){ if(obstacleGrid[i][j]!=-1) obstacleGrid[i][j]=obstacleGrid[i-1][j]*(obstacleGrid[i-1][j]>0? 1:0) + obstacleGrid[i][j-1]*(obstacleGrid[i][j-1]>0? 1:0); } } return obstacleGrid[m-1][n-1]>=0? obstacleGrid[m-1][n-1]:0; } };
Eg14:Jump Game
分析:这道题目与其说是DP,不如说是GA(贪心算法)。是的,这里又隐含一条我们以后要谈到的规律:谈心算法实际上是DP问题的加强版,每一个GA都可以用DP求解,反之不然。
我们仍然记得,DP问题的递推表达式类似:
dp[i][j]= max{****};如果对max内部包含的时间复杂度是O(n)或者O(i)、O(j)的大小;那么此时如果贪心算法能够使用,往往需要O(1)的算法即可。
分析二:如果使用DP,对本题而言,dp[i] ||= {dp[j]&&(j+A[j]>=i)}
但是,这样毕竟有些浪费,比如对于{1, 2, 3, 4, 5}很明显到i的时候,可以忽略前面部分的i-2部分的计算结果,都可以合并到i-1之中,于是就有了GA:
class Solution { public: bool canJump(int A[], int n) { if(n<2) return true; int maxi = A[0]; int i; for (i = 1; i < n; ++i){ if(maxi > 0){ maxi --; maxi = max(maxi, A[i]) ; } else return false; } return true; } };
Eg15:Jump Game II
本题和上题目类似,代码如下:
class Solution { public: int jump(int A[], int n) { if(n<2) return 0; int maxi=A[0] , step_left =0, res=0; for (int i = 1; i < n; ++i){ if(maxi > 0){ if(step_left > 0){ step_left--; } else{ res++; step_left = maxi-1; } maxi --; maxi = max(maxi, A[i]); } else return -1; } return res; } };
Eg16:Wildcard Matching
分析,这是一个双序列问题,典型的DP问题。但是,如果我们使用DP来求解,这道题目会超时。我们先来看看DP的解法。
dp[i+1][j+1]:s(0, i), t(0, j)是否能够匹配,res=dp[m]
;
递推关系就不写在这里了,这是一个2个方向的递归问题,需要注意的是时间和空间复杂度都是O(m*n), 因为实际测试的规模m, n在10^5, 所以没法通过。细细思考,有很多计算是不必要的;
计算dp[i+1][j+1], 仅仅需要dp[i][j+1], dp[i+1][j], dp[i][j].另外,如果是s="aaaaaaaaaa" t="aaaaaaaaaaaaaaaaaaa*"那么,我们计算dp[10][j],如果上一行左侧元素全部是0, 本行在0之前的计算都是冗余的。也就是说,我们仅仅需要从上一行不是0的地方开始算起。例如,上面的例子,dp[1][1]=true, dp[1][2]=false之后的dp[1][j]=false
是一直成立的。这样,这个问题就变成了一个回溯问题,见下面的代码。所以,这类求解存在性而非最优化的题目,使用DP往往不是最优的解法。 下面的这个解法,本质上是贪心算法,就是让固定长度的s匹配最长的t;如果失败,那么回溯到上一个*所在的位置。
class Solution { public: bool isMatch(const char *s, const char *p) { const char *prevs=s, *prev_start=NULL; while(*s){ if(*p=='?' || *p==*s){p++;s++;continue;} if(*p == '*') {prev_start=p; p++; prevs=s;continue;} if(prev_start) {p=prev_start+1; s= prevs+1;prevs++;continue;} return false; } while((*p)=='*') p++; return !(*p); } }; <span style="font-size:14px;"> </span>
需要注意的是,在这种模式下,"abc" 与 "a*"是匹配的。
相关文章推荐
- 动态规划第六讲——leetcode上的动态规划汇总(下)
- 动态规划第五讲——leetcode上的题目动态规划汇总(上)
- 动态规划第五讲——leetcode上的题目动态规划汇总(上)
- 动态规划 HDU 动态规划题集1\
- 通过金矿模型介绍动态规划 (很好的动态规划入门介绍)
- 动态规划与贪心算法区别以及如何思考动态规划
- CUGB 1050 题解动态规划简单题,这是我学会动态规划后做的第三道题比起前两道明显要顺手的多!秩序套用一下代码末班就行了。
- 动态规划专题:POJ 动态规划题目列表
- 动态规划小结 - 一维动态规划 - 时间复杂度 O(n),题 [LeetCode] Jump Game,Decode Ways
- 对动态规划原理理解的例子,先看看这个在看看动态规划剖析
- 动态规划与递推——动态规划是最优化算法 ( 转自2004chen)
- 动态规划(DP)的例题:动态规划的几类问题
- 杭电acm step 动态规划专题总结(1)简单的动态规划问题
- 【LeetCode】Jump Game (一维动态规划 + 线性扫描)
- 嵌套模型(DAG上的动态规划)—动态规划入门(算法经典入门)
- LeetCode题目:Unique Binary Search Trees,一维动态规划
- 【动态规划】树形动态规划专题
- 【LeetCode】Jump Game (一维动态规划 + 线性扫描)
- [ACM_动态规划] 轮廓线动态规划——铺放骨牌(状态压缩1)
- [LeetCode]wildcard matching通配符实现之动态规划