左神_基础提升班_07_暴力递归到动态规划01

1.机器人到达指定位置方法数
(1)暴力递归

	public static int ways1(int N, int M, int K, int 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,cur+1,rest-1,P);
		}
		// 如果还有rest步要走,而当前的cur位置在N位置上,那么当前这步只能从N走向N-1
		// 后续的过程就是,来到N-1位置上,还剩rest-1步要走
		if(cur==N){
			return walk(N,cur-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);
	}

(2)记忆化搜索:使用dp数组记录走过的位置

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

	private static int walk2(int N, int cur, int rest, int P, int[][] dp) {
		if(dp[cur][rest]!=-1){
			return dp[cur][rest];
		}
		if(rest==0){
			dp[cur][rest]=cur==P?1:0;
		}else if(cur==1){
			dp[cur][rest]=walk(N,cur+1,rest-1,P);
		}else if(cur==N){
			dp[cur][rest]=walk(N,cur-1,rest-1,P);
		}else{
			dp[cur][rest]=walk(N,cur-1,rest-1,P)+walk(N,cur+1,rest-1,P);
		}
		return dp[cur][rest];
	}

(3)严格表结构

	public static int ways3(int N, int M, int K, int P) {
		int dp[][]=new int[K+1][N+1];
		for(int i=0;i<K+1;i++){
			for (int j=0;j<N+1;j++){
				dp[i][j]=-1;
			}
		}
		return walk3(N,M,K,P,dp);
	}
	private static int walk3(int N, int cur, int rest, int P, int[][] dp){
		//第一行赋值
		for(int j=0;j<N+1;j++){
			if(j==P){
				dp[0][j]=1;
			}else{
				dp[0][j]=0;
			}
		}
		//其他行赋值
		for(int i=1;i<rest+1;i++){
			for (int j=1;j<N+1;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][j-1]+dp[i-1][j+1];
				}
			}
		}
		return dp[rest][cur];
	}

2.换钱的最少货币数(左神版:每一种面值的货币只能拿一个)
(1)暴力递归

	public static int minCoins3(int[] arr,int aim){
		return process3(arr,0,aim);
	}
	public static int process3(int[] arr,int i,int rest){
		if(rest<0){
			return -1;
		}
		if(rest==0){
			return 0;
		}
		if(i==arr.length){
			return -1;
		}
		int p1=process3(arr,i+1,rest);
		int p2Next=process3(arr,i+1,rest-arr[i]);
		if(p1==-1&&p2Next==-1){
			return -1;
		}else{
			if(p1==-1){
				return p2Next+1;
			}
			if(p2Next==-1){
				return p1;
			}
			return Math.min(p1,p2Next);
		}
	}

(2)记忆搜索

	public static int minCoins4(int[] arr,int aim){
		int dp[][]=new int[arr.length+1][aim+1];
		for (int i=0;i<arr.length;i++){
			for (int j = 0; j < aim; j++) {
				dp[i][j]=-2;
			}
		}
		return process4(arr,0,aim,dp);
	}
	public static int process4(int[] arr,int i,int rest,int[][] dp){
		if(rest<0){
			return -1;
		}
		if(dp[i][rest]!=-2){
			return dp[i][rest];
		}
		if(rest==0){
			dp[i][rest]=0;
			return dp[i][rest];
		}
		if(i==arr.length){
			dp[i][rest]=-1;
			return dp[i][rest];
		}
		int p1=process3(arr,i+1,rest);
		int p2Next=process3(arr,i+1,rest-arr[i]);
		if(p1==-1&&p2Next==-1){
			dp[i][rest]=-1;
		}else{
			if(p1==-1){
				dp[i][rest]=p2Next+1;
			}else if(p2Next==-1){
				dp[i][rest]=p1;
			}else{
				dp[i][rest]=Math.min(p1,p2Next);
			}
		}
		return dp[i][rest];
	}

(3)严格表

	public static int minCoin6(int [] arr,int aim){
		int dp[][]=new int[arr.length+1][aim+1];
		for(int index=0;index<=arr.length;index++){
			dp[index][0]=0;
		}
		for (int rest = 1; rest <=aim; rest++) {
			dp[arr.length][rest]=-1;
		}
		for(int index=arr.length-1;index>=0;index--){
			for (int rest=1;rest<=aim;rest++){
				int p1=dp[index+1][rest];
				int p2Next=-1;
				if(rest-arr[index]>=0){
					p2Next=dp[index+1][rest-arr[index]];
				}
				if(p1==-1&&p2Next==-1){
					dp[index][rest]=-1;
				}else{
					if(p1==-1){
						dp[index][rest]=p2Next+1;
					}else if(p2Next==-1){
						dp[index][rest]=p1;
					}else{
						dp[index][rest]=Math.min(p1,p2Next+1);
					}
				}
			}
		}
		return dp[0][aim];
	}

3.换钱的最少货币数(同一种面值可拿多个)
(1)暴力递归

	public static int minCoins1(int[] arr, int aim) {
		if(arr.length==0||arr==null||aim<0){
			return -1;
		}
		return process1(arr,0,aim);
	}

	// 当前考虑的面值是arr[i],还剩rest的钱需要找零
	// 如果返回-1说明自由使用arr[i..N-1]面值的情况下,无论如何也无法找零rest
	// 如果返回不是-1,代表自由使用arr[i..N-1]面值的情况下,找零rest需要的最少张数
	public static int process1(int[] arr, int i, int rest) {
		// base case:
		// 已经没有面值能够考虑了
		// 如果此时剩余的钱为0,返回0张
		// 如果此时剩余的钱不是0,返回-1
		if(i==arr.length){
			return rest==0?0:-1;
		}
		// 最少张数,初始时为-1,因为还没找到有效解
		int res=-1;
		// 依次尝试使用当前面值(arr[i])0张、1张、k张,但不能超过rest
			// 使用了k张arr[i],剩下的钱为rest - k * arr[i]
			// 交给剩下的面值去搞定(arr[i+1..N-1])
			 // 说明这个后续过程有效
		for(int k=0;k*arr[i]<=rest;k++){
			int next=process1(arr,i+1,rest-k*arr[i]);
			if(next!=-1){
				res=res==-1?next+k:Math.min(res,next+k);
			}
		}
		return res;
	}

(2)记忆化搜索

class Solution {
    public int coinChange(int[] coins, int amount) {
        if(coins.length == 0 || coins == null){
            return -1;
        }
        int dp[][] = new int[coins.length+1][amount+1];
        for(int i = 0; i <= coins.length; i++){
            for(int j = 0; j <= amount; j++){
                dp[i][j] = -2;
            }
        }
        return dfs(coins, 0, amount, dp);
    }
    public int dfs(int[] coins, int index, int rest, int [][] dp){
        if(dp[index][rest] != -2){	//如果当前位置之前计算过了,直接返回
            return dp[index][rest];
        }
        if(index == coins.length){	//在返回之前先记一下当前位置的值,再返回
            dp[index][rest] = rest == 0 ? 0 : -1;
            return dp[index][rest];
        }
        int res = -1;
        for(int i = 0; (i * coins[index]) <= rest; i++){
            int next = dfs(coins, index + 1, rest - (i * coins[index]), dp);
            if(next == -1){
                continue;
            }else{
                res = res == -1 ? next + i : Math.min(res, next + i);
            }
        }
        dp[index][rest] = res;//在返回之前先记一下当前位置的值,再返回
        return dp[index][rest];
    }
}

(3)严格表(未掌握)

	public static int minCoins3(int[] arr, int aim) {
		if (arr == null || arr.length == 0 || aim < 0) {
			return -1;
		}
		int N = arr.length;
		int[][] dp = new int[N + 1][aim + 1];
		// 设置最后一排的值,除了dp[N][0]为0之外,其他都是-1
		for (int col = 1; col <= aim; col++) {
			dp[N][col] = -1;
		}
		for (int i = N - 1; i >= 0; i--) { // 从底往上计算每一行
			for (int rest = 0; rest <= aim; rest++) { // 每一行都从左往右
				dp[i][rest] = -1; // 初始时先设置dp[i][rest]的值无效
				if (dp[i + 1][rest] != -1) { // 下面的值如果有效
					dp[i][rest] = dp[i + 1][rest]; // dp[i][rest]的值先设置成下面的值
				}
				// 左边的位置不越界并且有效
				if (rest - arr[i] >= 0 && dp[i][rest - arr[i]] != -1) {
					if (dp[i][rest] == -1) { // 如果之前下面的值无效
						dp[i][rest] = dp[i][rest - arr[i]] + 1;
					} else { // 说明下面和左边的值都有效,取最小的
						dp[i][rest] = Math.min(dp[i][rest],
								dp[i][rest - arr[i]] + 1);
					}
				}
			}
		}
		return dp[0][aim];
	}

3.换钱的最少货币数(同一种面值可拿多个,但是这道题并不是返回最少货币数,而是返回方法数)
(1)暴力递归

	public static int minCoin7(int [] arr,int aim){
		if(arr==null||arr.length==0||aim==0){
			return 0;
		}
		return process7(arr,0,aim);
	}

	private static int process7(int[] arr, int i, int rest) {
		if(i==arr.length){
			return rest==0?1:0;
		}
		int num=0;
		for(int zhang=0;zhang*arr[i]<=rest;zhang--){
			num+=process7(arr,i+1,rest-zhang*arr[i]);
		}
		return num;
	}

(2)记忆化搜索

	public static int minCoin8(int [] arr,int aim){
		if(arr==null||arr.length==0||aim==0){
			return 0;
		}
		int dp[][]=new int[arr.length+1][aim+1];
		return process8(arr,0,aim,dp);
	}

	private static int process8(int[] arr, int i, int rest,int [][] dp) {
		if(i==arr.length){
			dp[i][rest]=rest==0?1:0;
			return dp[i][rest];
		}
		int ways=0;
		for(int zhang=0;zhang*arr[i]<=rest;zhang--){
			ways+=process7(arr,i+1,rest-zhang*arr[i]);
		}
		dp[i][rest]=ways;
		return dp[i][rest];
	}

(3)严格表dp

	public static int minCoin9(int [] arr,int aim) {
		if (arr == null || arr.length == 0 || aim == 0) {
			return 0;
		}
		int dp[][] = new int[arr.length + 1][aim + 1];
		dp[arr.length][0] = 1;
		for (int index = arr.length - 1; index >= 0; index--) {
			for (int rest = 0; rest <= aim; rest++) {
				int ways = 0;
				for (int zhang = 0; arr[index] * zhang <= rest; zhang++) {
					if(rest - zhang  * arr[index]>=0){
						ways += dp[index + 1][rest - zhang * arr[index]];
					}
				}
				dp[index][rest]=ways;
			}
		}
		return dp[0][aim];
	}

(4)严格表dp(进一步优化)

	public static int minCoin10(int [] arr,int aim) {
		if (arr == null || arr.length == 0 || aim == 0) {
			return 0;
		}
		int dp[][] = new int[arr.length + 1][aim + 1];
		dp[arr.length][0] = 1;
		for (int index = arr.length - 1; index >= 0; index--) {
			for (int rest = 0; rest <= aim; rest++) {
				dp[index][rest]= dp[index + 1][rest];
				if(rest-arr[index]>=0){
					dp[index][rest]+=dp[index][rest-arr[index]];
				}
			}
		}
		return dp[0][aim];
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值