记忆化
递归的过程中做了重复工作,例如fib(3)计算了2次,其实只算1次就够了。为避免递归时重复计算,可以在子问题得到解决时,就保存结果,再次需要这个结果时,直接返回保存的结果就行了,不继续递归下去。这种存储已经解决的子问题结果的技术称为“记忆化“
记忆化是递归的常用优化技术。动态规划也常常用递归写代码,记忆化也是动态规划的关键技术
//斐波那契数列递归改进-》记忆化
public class code4 {
static int cnt = 0;//记录递归了多少次
static int[] data = new int[25];//存储斐波那契数
public static void main(String[] args) {
System.out.println(fib(20));
System.out.println(cnt);
}
public static int fib(int n) {
cnt++;
if (data[n]!=0) return data[n];//记忆化搜索:已经算过了
//递归出口
if(n==1||n==2) {
data[n] = 1;
return data[n];
}
data[n] = fib(n-1) + fib(n-2);
return data[n];
}
}

解决该题目的方式有很多,包括动态规划,枚举都可以解决这个问题
我们从递推的思想出发,假设我们从顶层沿着某条路径已经走到了第i层,正向着i+1层前进,两条可行路径中我们肯定会选择最大的方向前进,为此我们可以采用递推中的反向递推,即逆推的方式解决,设 a 存放从ij出发到达第n层的最大值。我们可以写出递推式:
a[i][j] = max{ a[i][j]+a[i+1][j] ,a[i][j]+a[i+1][j+1] }
则 逆推到出发点 a[1][1]为题目所求答案,即第一层到第 N层的最大值

思路:
1.dfs的变量=>每一次递归什么在变?
(1)当前数的大小一直在变:sum
(2)最高位的数:k
2.递归出口:最高位数字为1
3.注意:尽量使用全局变量
public class Main {
static int n = 0;
static int count = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
dfs(n,n);
System.out.println(count);
}
public static void dfs(int sum,int k) {
//sum表示:当前数的大小;k代表最高位的数
//递归出口
count++;
if (k==1) {
return;
}
for (int i = 1; i <= k/2; i++) {
dfs(sum*10+i,i);
}
}
}

思路:
1.dfs的变量=>每一次递归什么在变?
(1)m:已经选了几个数了
(2) last:已经遍历到哪一个数了(因为为了避免重复,排序都是从小到大)
(3)sum:当前选定的几个数的和
2.递归出口
(1)已经选定的数m == k (但是满足不一样就符合要求)
(2)看当前选定的几个数的和sum == n
3.注意:尽量使用全局变量
//数的划分
public class Main {
static int n;
static int k;
static int count=0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
k = sc.nextInt();
dfs(0, 1,0);
System.out.println(count);
}
public static void dfs(int m, int last,int sum) {
//m代表选取了m个数了;last表示遍历到哪个数了;sum代表目前已经累计多大的数了
if (m == k) {//递归出口
if (sum==n) count++;
return;
}
for (int i = last; i <= n-sum; i++) {
dfs(m+1,i,sum+i);
}
}
}

思路:
最佳策略就是第一次从中间,也就是m=n/2开始测,此时有两种情况:
1.第一部手机摔坏了。那么第二部手机只能从第一层慢慢的一层一层的向上测 此时b[i]=i-1+1=i (1是第一部手机测的那一次,i-1是第二部手机测的那i-1次)
2.第一部手机没摔坏。那么还剩两部手机可以从下一层往上测i-1次 此时b[i]=b[i-1]
b[i]=b[i-1]+i; //上一部手机没摔坏+上一部手机摔坏
3.第三部手机的分析跟第二部手机的分析一样,第三部手机相对于第二部手机来说是它的“第二部手机”
c[i]=c[i-1]+b[i-1]+1; //上一步手机没摔坏+上一部手机摔坏
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[] f2=new int[10010];//表示两步手机测i次的情况下能测的层数
int[] f3=new int[10010];//表示三步手机测i次的情况下能测的层数
int n = sc.nextInt();
int i = 0;
while(f3[i]<n) {
i++;
f2[i] = f2[i-1] + i;
f3[i] = f3[i-1] + f2[i-1] + 1;
}
System.out.println(i);
}
}
过多分支处理思路

这个题目是模拟法中最讨厌也最常见的一种,可能还有比这更复杂的,但这道题,已经初具代表性
他的种类比较多,天干就有10种,地支有12种
现在我们知道了 2020年是庚子年,我们这里既可以是除留余数来判断N年是什么天干和什么地支,我们也可以直接暴力使用循环做,这样的话 9999 的复杂度也跑不了多久。实现起来很简单,我们讲这个比较难的。
我们先判断0000年的天干和地支
根据题意0000年距2020年早了2020 年
已知天干有 10个,那么2020%10=0剩下的都是整个轮回,即到了0000年是庚X年,即天干是庚
再按照这个方法算地支是2020%12=4及还要向前推四年地支为申。
即0000年为康申年,那么根据模拟法可知
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
String[] str1 = {"geng", "xin", "ren", "gui", "jia", "yi", "bing", "ding", "wu", "ji"};
String[] str2 = {"shen", "you", "xu", "hai","zi", "chou", "yin", "mao", "chen", "si", "wu", "wei"};
System.out.println(str1[n%10]+str2[n%12]);
}
}
本文讨论了记忆化技术在递归算法中的应用,如斐波那契数列的优化,以及如何通过动态规划和递推思想解决数字三角形和数的计算问题。同时涉及了数的划分和耐摔指数问题的解决方案,展示了递归分支处理中天干地支的模拟方法。

被折叠的 条评论
为什么被折叠?



