暴力递归到动态规划
阶段:
尝试,递归 --》 记忆化搜索,DP --》 严格表结构,DP --》 严格表精版
某些问题上,记忆化搜索 和 严格表结构 用同样的时间复杂度。
机器人运动问题
一个整数n代表 你有n个位置,
一个整数s代表 开始的位置, 1到n之间。
一个整数e 代表 代表要去的 目标位置
一个整数k 代表 机器人必须走k步
走的规则: 1位置只能走向2, n位置下一步只能走向n-1
问: 走k步,从s到e有多少种方法?
展开表示:
暴力递归:
public static int ways1(int N, int M, int K, int P) {
// 参数无效直接返回0
if (N < 2 || K < 1 || M < 1 || M > N || P < 1 || P > N) {
return 0;
}
// 总共N个位置,从M点出发,还剩K步,返回最终能达到P的方法数
return walk(N, M, K, P);
}
// N : 位置为1 ~ N,固定参数
// cur : 当前在cur位置,可变参数
// rest : 还剩res步没有走,可变参数
// P : 最终目标位置是P,固定参数
// 该函数的含义:只能在1~N这些位置上移动,当前在cur位置,走完rest步之后,停在P位置的方法数作为返回值返回
public static int walk(int N, int cur, int rest, int P) {
// 如果没有剩余步数了,当前的cur位置就是最后的位置
// 如果最后的位置停在P上,那么之前做的移动是有效的
// 如果最后的位置没在P上,那么之前做的移动是无效的
if (rest == 0) {
return cur == P ? 1 : 0;
}
// 如果还有rest步要走,而当前的cur位置在1位置上,那么当前这步只能从1走向2
// 后续的过程就是,来到2位置上,还剩rest-1步要走
if (cur == 1) {
return walk(N, 2, rest - 1, P);
}
// 如果还有rest步要走,而当前的cur位置在N位置上,那么当前这步只能从N走向N-1
// 后续的过程就是,来到N-1位置上,还剩rest-1步要走
if (cur == N) {
return walk(N, N - 1, rest - 1, P);
}
// 如果还有rest步要走,而当前的cur位置在中间位置上,那么当前这步可以走向左,也可以走向右
// 走向左之后,后续的过程就是,来到cur-1位置上,还剩rest-1步要走
// 走向右之后,后续的过程就是,来到cur+1位置上,还剩rest-1步要走
// 走向左、走向右是截然不同的方法,所以总方法数要都算上
return walk(N, cur + 1, rest - 1, P) + walk(N, cur - 1, rest - 1, P);
}
第一步优化: 记忆化搜索,就加了缓存
第二步优化: 动态规划,纠结位置依赖顺序
严格表结构的动态规划。
时间复杂度:
硬币问题
leetcode322
给你一个正数数组,里面每一个值,代表一枚硬币的面值。
一个数代表一枚硬币。
aim = 10.
组成这个10,最少用多少硬币。 7 + 3
递归:
package com.wanghaha.algorithm;
public class Day8_03_62CoinsMin {
public static int minCoins1(int[] arr, int aim){
process(arr, 0, aim);
}
// arr[index..]组成出rest这么多钱,最少的硬币数量返回
public static int process(int[] arr, int index, int rest){
if(rest < 0){
return -1;
}
if( rest == 0 ){
return 0;
}
// rest > 0
if(index == arr.length ){
return -1;
}
// rest > 0 and 也有硬币
int p1 = process(arr, index +1 , rest);
int p2Next = process(arr, index + 1 , rest - arr[index]);
if(p1 == -1 && p2Next == -1){
return -1;
}else {
if(p1 == -1){
return p2Next + 1 ;
}
if(p2Next == -1 ){
return p1;
}
}
return Math.min(p1, p2Next);
}
}
记忆化搜索
public static int minCoins2(int[] arr, int aim){
int[][] dp = new int[arr.length +1 ][aim + 1];
for (int i = 0; i < arr.length + 1; i++) {
for (int j = 0; j < aim + 1; j++) {
dp[i][j] = -2;
}
}
return process2(arr, 0, aim, dp);
}
// arr[index..]组成出rest这么多钱,最少的硬币数量返回
public static int process2(int[] arr, int index, int rest,int[][] dp){
if(rest < 0){
return -1;
}
if(dp [index][rest] != -2 ){
return dp[index][rest];
}
if( rest == 0 ){
dp[index][rest] = 0;
}