暴力递归
尝试函数(题目涉及的参数)
if(边界条件)
return 尝试函数(改变的参数)
例:机器人走路
//N长度
//start开始的位置
//K只能走几步
//aim目的地
public static int ways1(int N, int start, int K, int aim){
return process1(start,K,aim,N);
}
//cur当前位置
//rest剩余步数
//aim目的地
//N长度
public static int process1(int cur, int rest, int aim, int N){
//边界条件
//若步数已走完时
if(rest == 0){
//当当前位置与目的地一致,则是一种方法返回1,反之则不是返回0
return cur == aim?1:0;
}
//若当前位置在最左侧时
if(cur == 1){
//则只能往右侧走,下一步的当前位置为2,剩余步数少一
return process1(2,rest-1,aim,N);
}
//若当前位置在最右侧时
if(cur == N){
//则只能往左侧走,下一步的当前位置为N-1,剩余步数少一
return process1(N-1,rest-1,aim,N);
}
//若当前位置在中间段时,可以往两边走
return process1(cur-1,rest-1,aim,N)+process1(cur+1,rest-1,aim,N);
}
傻缓存
加入表[变化的参数1][变化的参数2]为函数参数
将已求过的值缓存入表中
public static int ways2(int N, int start, int K, int aim){
//dp为缓存表
int[][] dp = new int[N+1][K+1];
for(int i = 0; i <=N; i++){//行
for(int j = 0; j <=N; j++){//列
dp[i][j] = -1;
}
}
return process2(start,K,aim,N,dp);
}
public static int process2(int cur, int rest, int aim, int N, int[][] dp){
//边界条件
//根据上面的尝试函数可知,cur和rest参数一直在改变,则表中的参数则为cur和rest
//若dp表对应的参数曾被算过时,由于之前被存储进去过,则dp表中必含有该参数不是上面方法赋值-1的值
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 = process2(2,rest-1,aim,N,dp);
}else if(cur == N){
ans = process2(N-1,rest-1,aim,N,dp);
}else{
ans = process1(cur-1,rest-1,aim,N)+process1(cur+1,rest-1,aim,N,dp);
}
//将ans值缓存放入dp中
dp[cur][rest] = ans;
return ans;
}
动态规划(记住已求过的解,减少重复计算)
定义表[变化的参数1][变化的参数2]
根据尝试函数画出表图,把原来递归函数用表来代替
例:N:123,start:2,aim:3,K:3,则求坐标(2,3)的值
return process1(cur-1,rest-1,aim,N) +process1(cur+1,rest-1,aim,N); | if(rest == 0){ return cur==aim?1:0; } | ||||
cur\rest | 0 | 1 | 2 | 3 | |
0 | × | × | × | × | |
if(cur == 1){ return process1(2,rest-1,aim,N); } | 1 | 0 | 0 依赖于 (2,1-1) | 1 | 0 |
2 | 0 | 1 依赖于 (2-1,1-1) +(2+1,1-1) | 0 | ※求 2 | |
if(cur == N){ return process1(N-1,rest-1,aim,N); } | 3 | 1 | 0 依赖于 (3-1,1-1) | 1 | 0 |
public static int ways3(int N, int start, int P, int K){
//定义表
int[][] dp = new int[N+1][K+1];
//根据上面的尝试函数可知
/*
if(rest == 0){
return cur == aim?1:0;
}
*/
dp[P][0] = 1;//dp[除P外][0] = 0
for(int rest = 1; rest <= K; rest++){//列
/*
if(cur == 1){
return process1(2,rest-1,aim,N);
}
*/
dp[1][rest] = dp[2][rest-1];
for(int cur = 2; cur < N; cur++){//行
//return process1(cur-1,rest-1,aim,N)+process1(cur+1,rest-1,aim,N);
dp[cur][rest] = dp[cur-1][rest-1] + dp[cur+1][rest-1];
}
/*
if(cur == N){
return process1(N-1,rest-1,aim,N);
}
*/
dp[N][rest] = dp[N-1][rest-1];
}
return dp[start][K];
}