1.什么暴力递归可以继续优化
有重复调用同一个子问题的解,这种递归可以优化;如果每一个子问题都是不同的解,无法优化也不用优化。
2.设计暴力递归的原则
1)每一个可变参数的类型,不要比int类型更加复杂
2)原则1)可以违反,让类型突破到一维线性结构,必须是单一可变参数
3)如果发现原则1)被违反,但不违反原则2),只需做到记忆化搜索即可
4)可变参数的个数能少则少
3.常见的4种尝试模型
1)从左往右的尝试模型
2)范围上的尝试模型
3)多样本位置全对应的尝试模型
4)寻找业务限制的尝试模型
4.暴力递归到动态规划
1)有一个不违反规则的暴力递归,而且的确存在解的重复调用
2)找到哪些参数的变化会影响返回值,对每一个列出变化范围
3)参数间的所有的组合数量,意味着表的大小
4)规定好严格表的大小,分析位置的依赖顺序,从基础填到最终解
5)对于有枚举行为的决策过程,进一步优化
5.什么是记忆化搜索
记忆化搜索的核心思想是在搜索过程中,对于已经计算过的状态,将其结果保存下来,以便在后续的计算中可以直接使用,从而避免重复计算。
记忆化搜索通过创建一个缓存(通常是一个数组或哈希表)来存储子问题的结果。当再次遇到相同的子问题时,算法会直接从缓存中读取结果,而不是重新计算。这样可以显著减少总的计算次数,提高算法的运行速度。
记忆化搜索的实质是动态规划,效率也和动态规划接近,但形式是搜索,更加简单直观,代码也容易编写。同时,记忆化搜索在求解时按自顶向下的顺序进行,每求解一个状态就将其解保存下来,以便后续使用。
6.实例
在一个1-N的一维轴上有一个机器人,机器人现在在cur位置,机器人需要用ret步去到达aim位置,问:共有多少种方法?
暴力递归:
public static int ways(int N,int cur,int aim,int rest){
return process(N,cur,aim,rest);
}
public static int process(int N,int cur,int aim,int rest){
if(rest==0){//没有步数可以再移动了
return cur==aim?1:0;
}
if(cur==1){//边界条件:到达最左端
return process(N,2,aim,rest-1);//只能向右走,否则越界
}
if(cur==N){//边界条件:到达最右端
return process(N,N-1,aim,rest-1);//只能向左走,否则越界
}
return process(N,cur-1,aim,rest-1)+process(N,cur+1,aim,rest-1);
//已经判断过边界条件,因此不再看是否越界
}
记忆化搜索:
public static int way2(int N,int cur,int aim,int rest){
int[][] dp = new int[N+1][rest+1];
for(int i=0;i<=N;i++){
for(int j=0;j<=rest;j++){
dp[i][j]=-1;
}
}
return process(N,cur,aim,rest,dp);
}
public static int process(int N,int cur,int aim,int rest,int[][] dp){
if(dp[cur][rest]!=-1){//代表已经算过了,不需要再算,直接拿值
return dp[cur][rest];
}
//之前没算过
int ans=0;
if(rest==0){
ans=cur==aim?1:0;
}else if(cur==1){
ans=process(N,2,aim,rest-1,dp);
}else if(cur==2){
ans=process(N,N-1,aim,rest-1,dp);
}else{
ans=process(N,cur-1,aim,rest-1,dp)+process(N,cur+1,aim,rset-1,dp);
}
dp[cur][rest]=ans;//填表
return ans;
}
动态规划:
1)画表格找依赖关系
2)先填好边界条件,以已知为基础
public static int ways3(int N,int cur,int aim,int rest){
return process(N,cur,aim,rest);
}
public static int process(int N,int cur,int aim,int rest){
int[][] dp=new int[N+1][rest+1];
dp[aim][0]=1;//当rest为0时,只有目标位置才是1
for(int i=1;i<=rest;rest++){
dp[1][i]=dp[2][i-1];
for(j=2;j<N;j++){
dp[j][i]=dp[j-1][i-1]+dp[j+1][i-1];
}
dp[N][i]=dp[N-1][i-1];
}
return dp[cur][rest];
}