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

news2024/11/24 13:29:23

请添加图片描述

⭐️前言⭐️

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

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

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

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


请添加图片描述

📍内容导读📍

  • 🍅最小距离和
  • 🍅目标货币值
  • 🍅目标货币值2
  • 🍅目标货币值3
  • 🍅醉汉走路

🍅最小距离和

题目:
给定一个二维数组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!!如有问题,欢迎评论区批评指正😁

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/628157.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Python快速创建列表

Python快速创建列表 在Python编程中&#xff0c;列表是一种非常常见的数据类型&#xff0c;它可以容纳多个值。创建列表有多种方式&#xff0c;但是在不同场景下&#xff0c;我们需要使用不同的方法来创建一个高效的列表。本文主要介绍如何快速创建列表的不同方法。我们将深入…

K-verse 合作伙伴访谈|与 Studio Dragon 一起进入韩剧元宇宙世界

穿越时空的韩剧元宇宙。 Studio Dragon 是全球排名第一的生活创作者 CJ ENM 的子公司&#xff0c;是引领韩剧的韩国代表性戏剧工作室&#xff0c;一个以无限故事内容让世界着迷的优质故事讲述者。 通过与 The Sandbox 的合作&#xff0c;我们将提供一种全新体验&#xff0c;让用…

openGauss5.0企业版使用指南之系统架构

文章目录 1. 产品定位2. 3.x版本和5.x版本比对3. openGauss 5.0版本架构4. openGauss 5.0 特点 背景&#xff1a;今年3月openGauss 5.0发布&#xff0c;升级了资源池化内核能力和DataKit数据全生命周期管理工具&#xff0c;整体在性能、安全性与易用性方面均有大幅提升。本次大…

vue-admin-template_home增加全屏开关

1. 安装 npm install screenfull --save 这个指令安装最新版本 npm install screenfull3 VUE2.x的可以指定对应的版本&#xff0c;这样是安装最新的3.x的版本 2. 导入svg文件 在src\icons\svg文件夹下&#xff0c;导入exit-fullscreen.svg和fullscreen.svg, exit-fullsc…

Git仓库相关操作

目录 Git作用 集中式 分布式 Git操作Git区域概念 Git命令 远程仓库 新建项目 新建仓库 克隆项目 推送项目 拉取项目 Git作用 作用&#xff1a;版本控制多人协作 集中式 典型代表&#xff1a;SVN 特点&#xff1a;所有的版本库都存在中央服务器&#xff0c;本地备份…

线性代数3:矩阵

目录 矩阵研究的是什么呢&#xff1f; 逆阵 什么叫做逆阵&#xff1f; 例题1&#xff1a; 例题2&#xff1a; 逆阵的存在性 定理1&#xff1a; 定理2&#xff1a; 定理3&#xff1a; 定理4&#xff1a; 拉普拉茨方程 方阵可以的条件 例题3&#xff1a; Note1&#xff…

Hive 巡检工具-对表数据量、主键重复数量以及每个字段标签的空值检测

目录 背景 巡检工具 数据准备 1、准备一张配置信息表&#xff0c;该表保存需要巡检的数据信息&#xff08;规则code不可重复&#xff09; 2、pyspark代码编写 结果表数据展示 规则自动检测并自增 数据准备 背景 该需求是利用pyspark对部分重点产出表进行数据质量监控。主…

如何使用二三层仪表模拟无状态的DDOS攻击测试

什么是DDOS攻击 分布式拒绝服务攻击(Distributed Denial of Service&#xff0c;简称DDoS)是指通过大规模互联网流量淹没目标服务器或其周边基础设施&#xff0c;以破坏目标服务器、服务或网络正常流量的恶意行为。 大量虚假的用户占用网络资源&#xff0c;把资源耗尽&#x…

PREP黄金沟通法则

PREP黄金沟通法则 掌握PREP黄金沟通四步法则&#xff0c;改善沟通困局&#xff0c;让交流更高效&#xff01; 模型介绍 Point: 结论先行让对方第一时间知道你想表达的观点。Reason: 摆出依据摆出你观点的依据&#xff0c;要做到客观公正、统一度量、表达准确、不出现歧义。Exa…

软件测试的案例分析 - 闰年4.2

这篇博客的目录 文章目的正文错误之一出错后怎么改正&#xff1f;正确而简明的算法 文章目的 显示不同的博客能获得多少博客质量分 &#xff08;这是关于博客质量分的测试 https://www.csdn.net/qc) 这个博客得了 60 分。 希望在新的质量分系统中&#xff0c;获得 80 - 90 分左…

Goby 漏洞更新 |MDT KNX 管理面板默认口令

漏洞名称&#xff1a;MDT KNX 管理面板默认口令 English Name&#xff1a;MDT KNX manager panel default credentials vulnerability CVSS core: 7.5 影响资产数&#xff1a;1135 漏洞描述&#xff1a; MDT是一家智能楼宇自动化服务商&#xff0c;基于KNX技术进行产品制造…

互联网产品的帮助中心页面制作方法?

帮助中心&#xff08;Help Center&#xff09;是企业或组织为了向客户提供技术支持和解决方案而设立的一个资源库&#xff0c;为客户提供常见问题解答、使用指南、教程等信息&#xff0c;旨在提高客户满意度和降低客户支持成本。帮助中心通常提供多种服务方式&#xff0c;包括在…

企业级微服务架构实战项目--xx优选2

一 常用核心功能 1.1 mp返回分页工具类 1.2 返回统一的数据格式 第2部分 1.3 异常统一的处理 系统在运行过程中如果出现了异常&#xff0c;默认会直接返回异常信息&#xff0c;比如500错误提示。但是我们想让异常结果也显示为统一的返回结果对象&#xff0c;并且统一处理系统的…

Vue 组件化: 计算属性、内容分发、自定义事件

目录 1. 计算属性 1.1 计算属性的特点 2. 内容分发 2.1 使用插槽的示例 3. 自定义事件 1. 计算属性 什么是计算属性 ? 计算属性的重点突出在属性两字, 首先它是个属性, 其次这个属性有计算的能力, 这里的计算就是个函数; 简单来说, 它就是一个能够将计算结果缓存起来的属…

认识 SpringCloud 核心组件

✅作者简介&#xff1a;大家好&#xff0c;我是Cisyam&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Cisyam-Shark的博客 &#x1f49e;当前专栏&#xff1a; 微服务探索之旅 ✨特色专…

Linux进程间通信【共享内存】

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; Linux学习之旅 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 文章目录 &#x1f307;前言&#x1f3d9;️正文1、什么是共享内存&#xff1f;2、共享内存的相关知识2.1、共享内存的数据结构…

人工智能十大新星揭晓,华人学者占90%

人工智能领域著名杂志 IEEE Intelligent Systems发布了 2022 年度“人工智能十大新星”&#xff08;AIs 10 to Watch&#xff09;名单 &#xff0c;其中有九位都是华人研究者。知识人网小编推荐给大家。 近日&#xff0c;人工智能领域著名杂志 IEEE Intelligent Systems公布了 …

在JavaScript中的栈数据结构(Stack )

文章目录 导文什么是Stack 类&#xff1f;如何创建一个Stack如何修改Stack中的值栈声明方法举例添加移除查看查看栈顶元素检查栈是否为空检查栈的长度 清空栈元素打印栈元素 完整的Stack函数&#xff1a;创建Stack的其他方法-用 ES6 语法声明 Stack 类 使用Stack类在 JavaScrip…

关于GDPR体系文件介绍,介绍GDPR体系文件的内容和意义

随着数字化时代的到来&#xff0c;个人数据保护成为了一个日益受到关注的问题。欧盟于2018年5月25日颁布了“通用数据保护条例”&#xff08;GDPR&#xff09;&#xff0c;旨在加强对欧洲公民个人数据的保护。GDPR对企业和组织的数据保护和处理流程提出了严格的要求&#xff0c…

自助化打印面单教程

我们都知道&#xff0c;这几年快递行业&#xff0c;从传统纸质面单过渡到了电子面单。以往企业寄快递&#xff0c;能够自行填写纸质面单&#xff0c;等待收件员上门收件&#xff0c;现如今&#xff0c;企业寄件能否自行打印电子面单&#xff1f; 首先我们要先对比一下传统面单和…