暴力递归到动态规划

步骤:

1)找到什么可变参数可以代表一个递归状态,也就是哪些参数一旦确定,返回值就确定了

2)把可变参数的所有组合映射成一张表,有 1 个可变参数就是一维表,2 个可变参数就

是二维表,......

3)最终答案要的是表中的哪个位置,在表中标出

4)根据递归过程的 base case,把这张表的最简单、不需要依赖其他位置的那些位置填好

5)根据递归过程非base case的部分,也就是分析表中的普遍位置需要怎么计算得到,那

么这张表的填写顺序也就确定了

6)填好表,返回最终答案在表中位置的值

2.看题:

机器人达到指定位置方法数 假设有排成一行的 N 个位置,记为 1~N,N 一定大于或等于 2。开始时机器人在其中的 M 位 置上(M 一定是 1~N 中的一个),机器人可以往左走或者往右走,如果机器人来到 1 位置, 那 么下一步只能往右来到 2 位置;如果机器人来到 N 位置,那么下一步只能往左来到 N-1 位置。 规定机器人必须走 K 步,最终能来到 P 位置(P 也一定是 1~N 中的一个)的方法有多少种。给 定四个参数 N、M、K、P,返回方法数。

法一:递归

public class solution {

	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,res-1,P);
        }
        // 如果还有rest步要走,而当前的cur位置在N位置上,那么当前这步只能从N走向N-1
		// 后续的过程就是,来到N-1位置上,还剩rest-1步要走
        if(cur==N){
            return walk(N,N-1,res-1,P);
        }
        // 如果还有rest步要走,而当前的cur位置在中间位置上,那么当前这步可以走向左,也可以走向右
		// 走向左之后,后续的过程就是,来到cur-1位置上,还剩rest-1步要走
		// 走向右之后,后续的过程就是,来到cur+1位置上,还剩rest-1步要走
		// 走向左、走向右是截然不同的方法,所以总方法数要都算上
        return walk(N,cur+1,res-1,P)+walk(N,cur-1,res-1,P);
    }

}

优化一:递归过程有很多重复的计算,加入二维表作缓存。

f(2,2,)重复计算

暴力递归到动态规划

 

public class solution {

	public static int ways2(int N, int M, int K, int P) {
		int[][] dp =new int [K+1][N+1];
        for(int i=0;i<=K;i++){
            for(int j=0;j<=N;j++){
                dp[i][j]=-1;
            }
        }

        // 参数无效直接返回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,dp);
	}
 
    public static int walk(int N , int cur , int rest, int P,int[][] dp){
        if(dp[rest][cur]!=-1){  //先判断计算的位置是否存在二维表中
            return dp[rest][cur];
        }
        if(rest==0){
            dp[rest][cur]=cur==P?1:0;
        }
        if(cur==1){
            dp[rest][cur]=walk(N,2,res-1,P);
        }
        else if(cur==N){
            dp[rest][cur]=walk(N,N-1,res-1,P)
        }
        else{
            dp[rest][cur]=walk(N,cur+1,res-1,P)+walk(N,cur-1,res-1,P)
        }
     
        return  dp[rest][cur];
    }

}

优化三:转为动态规划。

其实就是一个建表的过程。

初始赋值:cur无0位置 所以0列无数字。可以初始为-1。

当rest为0时,只有cur在终点位置时为1。

其余点的计算等于

暴力递归到动态规划

  public static int way3(int N, int M, int K, int P) {
        int[][] dp =new int [K+1][N+1];
        for(int i=1;i<=K;i++){
            for(int j=1;j<=N;j++){
                if(j==1){ //特殊边界处理
                    dp[i][j]=dp[i-1][2];
                }else if(j==N){
                    dp[i][j]=dp[i-1][N-1];
                }else{
                    dp[i][j]=dp[i-1][cur+1]+dp[i-1][cur-1];//与左上和右上的值有关
                }
            }
        }
        return dp[K][M];
    }

 题目二:换钱的最少货币数

给定数组 arr,arr 中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值 的货币可以使用任意张,再给定一个整数 aim,代表要找的钱数,求组成 aim 的最少货币 数。 首先还是暴力递归求解
class solution{
    public static int minCoins(int[] arr,int aim){
        if(arr=null || aim<0 || arr.length==0){
            return -1;
        } 
        return process(arr,0,aim); //每一个index都有拿不拿两种选择
    }
    // 当前考虑的面值是arr[i],还剩rest的钱需要找零
	// 如果返回-1说明*使用arr[i..N-1]面值的情况下,无论如何也无法找零rest
	// 如果返回不是-1,代表*使用arr[i..N-1]面值的情况下,找零rest需要的最少张数
    public static int process(int[] arr, int index, int rest){
        // 已经没有面值能够考虑了
		// 如果此时剩余的钱为0,返回0张
		// 如果此时剩余的钱不是0,返回-1
        if(res<0){
            return -1;
        }
        if(rest=0)
        {
            return 0;
        }
        if(i==arr.length){
            return rest==-1;
        }
        int p1=process(arr,index+1,rest);//不取
        int p2=process(arr,index+1,res-arr[index]);//取
        if(p1==-1 && p2==-1){
            return -1;
        }else{
            if(p1==-1){
                return p1=p2+1;
            }
            if(p2==-1){
                return p1;
            }
           return Math.min(p1,p2+1);

        }
    }
}

动态规划

暴力递归到动态规划

 

 public static int minCoins(int[] arr , int aim){
        int N =arr.length;
        int [][] dp= new int[N+1][aim+1];
//初始表赋值
        for(itn row=0;row<=N;row++){
            dp[row][0]=0;
        }
        for(int col=1;col<=aim;col++){
            dp[N][col]=-1;
        }
        for(int index=N-1;index>=0;index--){
            for(int rest=1;rest<=aim;rest++){
                int p1=dp[index+1][rest];
                int p2=-1;
                if(rest-arr[index]>=0){
                    p2=dp[index+1][rest-arr[index]];
                }
                if(p1==-1 && p2==-1){
                    dp[index][rest]=-1;
                }else{
                    if(p1==-1){
                        dp[index][rest]=p2+1;
                    }
                    else if(p2==-1){
                        dp[index][rest]=p1;
                    }else{
                        dp[index][rest]=Math.min(p1.p2+1);
                    }
                      
            }
        }
        return dp[0][aim];
    }

上一篇:使用python扫描文件夹获取所有文件路径


下一篇:Python - walk()方法详解2