首先必须确认的是凡是可以写出动态规划的递归表达式的都可以转化为记忆化搜索
原理
- 首先从递归表达式入手,对于一个斐波那契数列的问题,使用递归的方式可以进行解决,但是,这样类型的搜索是带有大量冗余的搜索,为了使得搜索的时候能够减小冗余,必须对搜索的过程进行记忆化。(以labuladong的原理图为例)
记忆化搜索
int fib(int N) {
if (N == 1 || N == 2) return 1;
return fib(N - 1) + fib(N - 2);
}
//递归的方式
//记忆化搜索
int fib(int N) {
// 备忘录全初始化为 0
int[] memo = new int[N + 1];
// 进行带备忘录的递归
return dp(memo, N);
}
// 带着备忘录进行递归
int dp(int[] memo, int n) {
// base case
if (n == 0 || n == 1) return n;
// 已经计算过,不用再计算了
if (memo[n] != 0) return memo[n];
memo[n] = dp(memo, n - 1) + dp(memo, n - 2);
return memo[n];
}
以上就是记忆化搜索的剪枝的过程
很明显的是记忆化搜索的过程是先确定了搜索的入口dp(int[] memo, int n),使用了int[]memo来记忆递归的状态,接下来是记忆化的过程
// 已经计算过,不用再计算了
if (memo[n] != 0) return memo[n];
通过记忆化,一旦已经计算过状态值,可以直接返回,从而减小了冗余的计算,这样复杂度从O(2^n)减小到了O(n)这实际上是一个巨大的进步。最后才是搜索的过程
memo[n] = dp(memo, n - 1) + dp(memo, n - 2);
return memo[n];
这就是说自底向下和自顶向下的算法的区别
自顶向下:
递归算法
自底向上:即由初始值到最终值
记忆化搜索、动态规划
相关例题
快速幂的递归解法
public double myPow(double x, int n) {
//快速霓算法,体现了分治和递归的思想,递归的方式
if(n==1){
return x;
}
return n>0? dfs(x,n):1/dfs(x,-n);
}
public double dfs(double x, int n){
if(n==0){
return 1;
}
double y=dfs(x,n/2);
return n%2==0 ? y*y:y*y*x;
}
快速幂的记忆化搜索的解法
public double dfs1(double x, int n){
if(n==0){
return 1;
}
if(dp.containsKey(n)){
return dp.get(n);
}
if(!dp.containsKey(n)){
dp.put(n,n%2==0 ? dfs1(x,n/2)*dfs1(x,n/2): dfs1(x,n/2)*dfs1(x,n/2)*x);
}
return dp.get(n);
}
Map<Integer, Double> dp;
public double myPow1(double x, int n) {
//快速霓算法,体现了分治和递归的思想,递归的方式
int k=Math.abs(n);
if(n==0){
return 1;
}
dp=new HashMap<>();
dp.put(0, 1.0);
dp.put(1,x);
return n>0 ? dfs1(x,k):1/dfs1(x,k);
}
最小路径和
动态规划的解法
public int minPathSum(int[][] grid) {
int n=grid.length;
int m=grid[0].length;
int [][] dp=new int[n][m];
dp[0][0]=grid[0][0];
for (int i = 1; i < n; i++) {
dp[i][0]=dp[i-1][0]+grid[i][0];
}
for (int i = 1; i < m; i++) {
dp[0][i]=dp[0][i-1]+grid[0][i];
}
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++) {
dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[n-1][m-1];
//记忆化搜索的方法写一下
}
记忆化搜索的解法
public int minPathSum(int[][] grid){
int m=grid.length;
int n=grid[0].length;
int [][]res=new int[m][n];
res[0][0]=grid[0][0];
for (int i = 1; i < n; i++) {
res[0][i]=grid[0][i]+res[0][i-1];
}
for (int i = 1; i < m; i++) {
res[i][0]=grid[i][0]+res[i-1][0];
}
return dfs5(res,m-1,n-1,grid);
}
public int dfs5(int [][]res,int i,int j,int[][] grid){
if(i<0 ||i>=res.length ||j<0 ||j>=res[0].length){
return 0;
}
if(res[i][j]>0){
return res[i][j];
}
res[i][j]=Math.min(dfs5(res,i-1,j,grid),dfs5(res,i,j-1,grid))+grid[i][j];
return res[i][j];
}