暴力递归到动态规划(三)

这篇具有很好参考价值的文章主要介绍了暴力递归到动态规划(三)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

暴力递归到动态规划(三)

⭐️前言⭐️

本篇文章是从暴力递归到动态规划的第三章。

🍉欢迎点赞 👍 收藏留言评论 📝私信必回哟😁

🍉博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言

🍉博客中涉及源码及博主日常练习代码均已上传GitHub


暴力递归到动态规划(三)

🍅最小距离和

题目:
给定一个二维数组matrix,一个人必须从左上角出发,最后到达右下角
沿途只可以向下或者向右走,沿途的数字都累加就是距离累加和
返回最小距离累加和

题解思路:
根据matrix表构建出dp表,dp表中数字的含义是从[0,0]位置到该位置的最短距离,dp表中的第一行只能依靠左边来得出,dp表中的第一列只能依靠上边来得出,其余位置的结果依赖左边和上边较小的数值,最后返回dp表右下角的结果即为所求。
暴力递归到动态规划(三)

代码实现:

public class MinPathSum {
    public static int minPathSum(int[][] m) {
        if(m==null||m.length==0||m[0]==null||m[0].length==0) {
            return 0;
        }
        int row=m.length;
        int col=m[0].length;
        int[][] dp=new int[row][col];
        dp[0][0]=m[0][0];
        for (int i = 1; i < row; i++) {
            dp[i][0]=dp[i-1][0]+m[i][0];
        }
        for (int j = 1; j < col; j++) {
            dp[0][j]=dp[0][j-1]+m[0][j];
        }
        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+m[i][j];
            }
        }
        return dp[row-1][col-1];
    }
}

题解思路2:(状态压缩)
如果matrix表非常大,那么对应的dp表也将会非常大,为了节省空间,可以通过一个一维数组来完成状态压缩,通过一行行更新数值的方式,到达最后一行的dp状态,返回最后结果。

首先第一行的状态都依赖于左边的数值,先完成第一行的初始化;往下的行中第一列,依赖于上边的值,此时数组中记录的就是上边的值,直接相加即可;然后右移,此时该位置的结果为上一行的结果,左边也刚完成更新,两者取较小再相加matrix中此位置的值即可。
代码实现:

public class MinPathSum {
    public static int minPathSum2(int[][] m) {
        if(m==null||m.length==0||m[0]==null||m[0].length==0) {
            return 0;
        }
        int row=m.length;
        int col=m[0].length;
        int[] dp=new int[col];
        dp[0]=m[0][0];
        for (int j = 1; j < col; j++) {
            dp[j]=dp[j-1]+m[0][j];
        }
        for (int i = 1; i < row; i++) {
            dp[0]+=m[i][0];
            for (int j = 1; j < col; j++) {
                dp[j]=Math.min(dp[j-1],dp[j])+m[i][j];
            }
        }
        return dp[col-1];
    }
}

🍅目标货币值

题目:
arr是货币数组,其中的值都是正数。再给定一个正数aim。
每个值都认为是一张货币,
即便是值相同的货币也认为每一张都是不同的,
返回组成aim的方法数
例如:arr = {1,1,1},aim = 2
第0个和第1个能组成2,第1个和第2个能组成2,第0个和第2个能组成2
一共就3种方法,所以返回3

题解思路1:
从左往右的尝试模型,每个位置考虑要或者不要,由两个变量确定终止条件,返回最终的结果。
代码实现:

public class CoinsWayEveryPaperDifferent {
    public static int coinWays(int[] arr,int aim) {
        return process(arr,0,aim);
    }
    
    // arr[index...] 组成正好rest这么多的钱,有几种方法
    public static int process(int[] arr,int index,int rest) {
        if(rest<0) {
            return 0;
        }
        if(index==arr.length) {
            return rest==0? 1: 0;
        }else {
            return process(arr,index+1,rest)+process(arr,index+1,rest-arr[index]);
        }
    }
}

题解思路2:
由暴力递归演变到动态规划,观察依赖关系,都是依赖index+1后一个位置的结果,所以dp表先完成后边的填写,再往前递推。

代码实现:

public class CoinsWayEveryPaperDifferent {
    public static int dp(int[] arr,int aim) {
        if(aim==0) {
            return 1;
        }
        int N=arr.length;
        int[][] dp=new int[N+1][aim+1];
        dp[N][0]=1;
        for (int index = N-1; index >=0 ; index--) {
            for (int rest = 0; rest <=aim ; rest++) {
                dp[index][rest]=dp[index+1][rest]+(rest-arr[index]>=0?dp[index+1][rest-arr[index]]:0);
            }
        }
        return dp[0][aim];
    }
}

🍅目标货币值2

题目:
arr是面值数组,其中的值都是正数且没有重复。再给定一个正数aim。
每个值都认为是一种面值,且认为张数是无限的。
返回组成aim的方法数
例如:arr = {1,2},aim = 4
方法如下:1+1+1+1、1+1+2、2+2
一共就3种方法,所以返回3

题解思路1:
依然是从左往右的模型,依次考虑每个位置面值的张数情况,从而确定方法数。

代码实现:

public class CoinsWayNoLimit {
    public static int coinsWay(int[] arr,int aim) {
        if(arr==null || arr.length==0 || aim<0) {
            return 0;
        }
        return process(arr,0,aim);
    }
    
    // arr[index...]所有的面值,每一个面值都可以任意选择张数,组成正好rest这么多钱,求方法数有多少?
    public static int process(int[] arr,int index,int rest) {
        if(index==arr.length) {
            return rest==0?1:0;
        }
        int ways=0;
        for (int pages = 0; pages*arr[index] <=rest ; pages++) {
            ways+=process(arr,index+1,rest-(pages*arr[index]));
        }
        return ways;
    }
}

题解思路2:
根据暴力递归的尝试,转化为动态转移方法,根据严格依赖的表关系,得出最后的结果。

代码实现:

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

题解思路3:
由于每个位置的dp结果,依赖于for循环的多个结果,可以通过记忆化搜索的方式再进一步优化,如下所示:
暴力递归到动态规划(三)

假设星所在行对应的面值为2,那么黄星依赖于下边三个圆圈的结果;红星依赖于第一个和第二个结果,所以黄星可以通过红星+dp[index+1][rest]得到,而避免再去通过for循环来计算。
代码实现:

public class CoinsWayNoLimit {
    public static int dp2(int[] arr,int aim) {
        if(arr==null||arr.length==0||aim<0) {
            return 0;
        }
        int N=arr.length;
        int[][] dp=new int[N+1][aim+1];
        dp[N][0]=1;
        for (int index = N-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];
    }
}

🍅目标货币值3

题目:
arr是货币数组,其中的值都是正数。再给定一个正数aim。
每个值都认为是一张货币,
认为值相同的货币没有任何不同,
返回组成aim的方法数
例如:arr = {1,2,1,1,2,1,2},aim = 4
方法:1+1+1+1、1+1+2、2+2
一共就3种方法,所以返回3
题解思路:
该题就是目标货币值2加了限制条件,每个货币不是无限张了,而是有限张数。

代码实现:

public class CoinsWaySameValueSamePapper {
    public static class Info {
        public int[] coins;
        public int[] pages;

        public Info(int[] coins, int[] pages) {
            this.coins = coins;
            this.pages = pages;
        }
    }

    public static Info getInfo(int[] arr) {
        HashMap<Integer,Integer> counts=new HashMap<>();
        for (int value:arr) {
            if(!counts.containsKey(value)) {
                counts.put(value,1);
            }else {
                counts.put(value,counts.get(value)+1);
            }
        }
        int N=counts.size();
        int[] coins=new int[N];
        int[] pages=new int[N];
        int index=0;
        for (Map.Entry<Integer,Integer> entry: counts.entrySet()) {
            coins[index]=entry.getKey();
            pages[index++]= entry.getValue();
        }
        return new Info(coins,pages);
    }
    
    public static int coinsWay(int[] arr,int aim) {
        if(arr==null||arr.length==0||aim<0) {
            return 0;
        }
        Info info=getInfo(arr);
        return process(info.coins,info.pages,0,aim);
    }
    
    // coins 面值数组,正数且去重
    // pages 每种面值对应的张数
    public static int process(int[] coins,int[] pages,int index,int rest) {
        if(index==coins.length) {
            return rest==0?1:0;
        }
        int ways=0;
        for (int page=0;page*coins[index]<=rest&&page<=pages[index];page++) {
            ways+=process(coins,pages,index+1,rest-(page*coins[index]));
        }
        return ways;
    }
    
    public static int dp1(int[] arr,int aim) {
        if(arr==null||arr.length==0||aim<0) {
            return 0;
        }
        Info info=getInfo(arr);
        int[] coins= info.coins;
        int[] pages= info.pages;
        int N=coins.length;
        int[][] dp=new int[N+1][aim+1];
        dp[N][0]=1;
        for (int index = N-1; index >=0 ; index--) {
            for (int rest = 0; rest <=aim ; rest++) {
                int ways=0;
                for (int page = 0; page*coins[index]<=rest&&page<=pages[index] ; page++) {
                    ways+=dp[index+1][rest-page*coins[index]];
                }
                dp[index][rest]=ways;
            }
        }
        return dp[0][aim];
    }
    
    public static int dp2(int[] arr,int aim) {
        if(arr==null||arr.length==0||aim<0) {
            return 0;
        }
        Info info=getInfo(arr);
        int[] coins= info.coins;
        int[] pages=info.pages;
        int N=coins.length;
        int[][] dp=new int[N+1][aim+1];
        dp[N][0]=1;
        for (int index = N-1; index >=0 ; index--) {
            for (int rest = 0; rest <=aim ; rest++) {
                dp[index][rest]=dp[index+1][rest];
                if(rest-coins[index]>=0) {
                    dp[index][rest]+=dp[index][rest-coins[index]];
                }
                // 如果记忆化搜索可能会多算,再把多算的减掉即可
                if(rest-coins[index]*(pages[index]+1)>=0) {
                    dp[index][rest]-=dp[index+1][rest-coins[index]*(pages[index]+1)];
                }
            }
        }
        return dp[0][aim];
    }
}

🍅醉汉走路

题目:
给定5个参数,N,M,row,col,k
表示在NM的区域上,醉汉Bob初始在(row,col)位置
Bob一共要迈出k步,且每步都会等概率向上下左右四个方向走一个单位
任何时候Bob只要离开N
M的区域,就直接死亡
返回k步之后,Bob还在N*M的区域的概率

题解思路:
每个位置可以走向四个方向,总结果数为4^k,如果走完K步,没有走出N*M区域,就返回一种结果,最后得出生存点数除以总结果数,即为所求。

代码实现:

public class BobDie {
    public static double livePosibility1(int row,int col,int k,int N,int M) {
        return (double) process(row,col,k,N,M)/Math.pow(4,k);
    }

    // 目前在row,col位置,还有rest步要走,走完了如果还在棋盘中就获得1个生存点,返回总的生存点数
    public static long process(int row,int col,int rest,int N,int M) {
        if(row<0||row==N||col<0||col==M) {
            return 0;
        }
        if(rest==0) {
            return 1;
        }
        long up=process(row-1,col,rest-1,N,M);
        long down=process(row+1,col,rest-1,N,M);
        long left=process(row,col-1,rest-1,N,M);
        long right=process(row,col+1,rest-1,N,M);
        return up+down+left+right;
    }
}

题解思路2:
根据暴力递归转化为动态规划,可变参数有row、col、rest三个,建立一个三维dp表,根据依赖关系记录每个位置的结果,最后返回结果。

代码实现:

public class BobDie {
    public static double livePosibility2(int row,int col,int k,int N,int M) {
        long[][][] dp=new long[N][M][k+1];
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                dp[i][j][0]=1;
            }
        }
        for (int rest = 1; rest <=k ; rest++) {
            for (int r = 0; r < N; r++) {
                for (int c = 0; c < M; c++) {
                    dp[r][c][rest]=pick(dp,N,M,r-1,c,rest-1);
                    dp[r][c][rest]+=pick(dp,N,M,r+1,c,rest-1);
                    dp[r][c][rest]+=pick(dp,N,M,r,c-1,rest-1);
                    dp[r][c][rest]+=pick(dp,N,M,r,c+1,rest-1);
                }
            }
        }
        return (double)dp[row][col][k]/Math.pow(4,k);
    }

    public static long pick(long[][][] dp,int N,int M,int r,int c,int rest) {
        if(r<0||r==N||c<0||c==M) {
            return 0;
        }
        return dp[r][c][rest];
    }
}

⭐️最后的话⭐️
总结不易,希望uu们不要吝啬你们的👍哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正😁

暴力递归到动态规划(三)文章来源地址https://www.toymoban.com/news/detail-489159.html

到了这里,关于暴力递归到动态规划(三)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法27:从暴力递归到动态规划(2)

    上一题比较简单,下面来一道比较难的题目。 假设有排成一行的 N 个位置,记为 1~ N , N 一定大于或等于 2 开始时机器人在其中的 M 位置上 ( M 一定是 1~ N 中的一个 ) 如果机器人来到 1 位置,那么下一步只能往右来到 2 位置; 如果机器人来到 N 位置,那么下一步只能往左来到

    2024年02月09日
    浏览(28)
  • 算法7.从暴力递归到动态规划0

    题意:打印n层汉诺塔从最左边移动到最右边的全部过程 解题思路: 把字母抛掉,变成左中右三个盘子 多个盘子能一下到吗?不能,把上边的拿走,最下边的才能放到指位置(leftToRight) 上边的怎么拿,leftToMid。那它具体怎么拿,midToLeft…如此,需要6个子过程 他们之间互相调

    2024年02月08日
    浏览(25)
  • 算法12.从暴力递归到动态规划5

    题意:假设有排成一行的N个位置记为1~N,N一定大于或等于2 开始时机器人在其中的M位置上(M一定是1~N中的一个) 如果机器人来到1位置,那么下一步只能往右来到2位置; 如果机器人来到N位置,那么下一步只能往左来到N-1位置; 如果机器人来到中间位置,那么下一步可以往左

    2024年02月20日
    浏览(32)
  • 一篇文章带你搞懂动态规划(由暴力递归到动态规划)

    ”动态规划“ 的过程相当于 记忆化搜索 , 即在普通 递归 的过程中用二维数组进行记忆化存储一些状态结果, 从而避免重复的计算(剪枝)。 举一个简单的例子:在 递归法 求解 斐波那契 数列的过程中, 就进行了多次的 重复计算 , 而动态规划相当于是对已经计算的状态

    2024年02月20日
    浏览(44)
  • 题解 | #上台阶#C++暴力动态规划解法,非递归

    25届想找实习求看看简历 英伟达笔试 Nvidia24秋招 英伟达嵌入式软件工程师笔试 9-26 2022-08-17-nvidia实习 我发现算法岗也不很难进啊(深度学习) 我发现算法岗也不很难进啊(深度学习) 顺丰科技 1.30校招实习招聘信息汇总 2024春招汇总 『 哨哥的校园招聘周报 』02/05 - 02/18 深圳银河创

    2024年02月21日
    浏览(28)
  • 爬楼梯问题-从暴力递归到动态规划(java)

    一个总共N 阶的楼梯(N 0) 每次只能上一阶或者两阶。问总共有多少种爬楼方式。 示例1: N = 1, 一步上去了,返回1. 示例2: N = 2时。 可以第一次上一阶,再上一阶,这是一种方式, 也可以一次直接上两阶,这也是一种方式, 返回 2; 示例3: N = 3: 可以选择, 1 1 1, 1

    2024年02月10日
    浏览(28)
  • 【LeetCode:72. 编辑距离 | 暴力递归=>记忆化搜索=>动态规划 】

    🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,CSDN-Java领域新星创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文

    2024年02月06日
    浏览(31)
  • 【LeetCode:64. 最小路径和 | 暴力递归=>记忆化搜索=>动态规划 】

    🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,CSDN-Java领域新星创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文

    2024年02月05日
    浏览(31)
  • 零钱兑换,凑零钱问题,从暴力递归到动态规划(java)

    322 零钱兑换 - 可以打开链接测试 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种硬币的数量是无限的。 示例 1:

    2024年02月07日
    浏览(29)
  • 【LeetCode: 44. 通配符匹配 | 暴力递归=>记忆化搜索=>动态规划 】

    🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,CSDN-Java领域优质创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文

    2024年02月06日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包