滑动窗口系列4-Leetcode322题零钱兑换-限制张数-暴力递归到动态规划再到滑动窗口

这个题目是Leecode322的变种,322原题如下:

我们这里的变化是把硬币变成可以重复的,并且只有coins数组中给出的这么多的金币,也就是说有数量限制: 

package dataStructure.leecode.practice;import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;public class CoinsChange {public static int coinChange(int[] coins, int amount) {if(amount == 0) return 0;int nums = process(coins, 0, amount);return nums == Integer.MAX_VALUE? -1 : nums;}/*** 最傻的递归* @param coins* @param curIndex* @param restAmount* @return*/public static int process(int[] coins, int curIndex, int restAmount) {if(restAmount == 0) return 0;if(curIndex == coins.length) return Integer.MAX_VALUE;//两种可能性:用当前位置的数字或者不用当前位置的数字int p1 = process(coins, curIndex + 1, restAmount);int ans = p1;int p2 = process(coins, curIndex + 1, restAmount - coins[curIndex]);if(p2 != Integer.MAX_VALUE) {ans = Math.min(ans, p2 + 1);}return ans;}/*** 最傻的递归改的动态规划* @param coins* @return*/public static int coinChangeDp(int[] coins, int amount) {int N = coins.length;int[][] dp = new int[N + 1][amount + 1];for(int j = 1; j <= amount; j++) {dp[N][j] = Integer.MAX_VALUE;}for(int curIndex = N - 1; curIndex >= 0; curIndex --) {for(int restAmount = 0; restAmount <= amount; restAmount ++) {//两种可能性:用当前位置的数字或者不用当前位置的数字int p1 = dp[curIndex + 1][restAmount];int ans = p1;if(restAmount - coins[curIndex] >= 0) {int p2 = dp[curIndex + 1][restAmount - coins[curIndex]];if(p2 != Integer.MAX_VALUE) {ans = Math.min(ans, p2 + 1);}}dp[curIndex][restAmount] = ans;}}return dp[0][amount];}public static int coinChange2(int[] coins, int amount) {if(amount == 0) return 0;CoinsInfo coinsInfo = getCoinsInfo(coins);int nums = process2(coinsInfo.values, coinsInfo.nums, 0, amount);return nums == Integer.MAX_VALUE? -1 : nums;}public static CoinsInfo getCoinsInfo(int[] coins) {//使用HashMap做频次的统计HashMap<Integer, Integer> count = new HashMap<>();//Arrays.sort(coins);//遍历coins的每个硬币并进行统计for (int coin : coins) {if(count.containsKey(coin)) {count.put(coin,count.get(coin) + 1);} else {count.put(coin, 1);}}//初始化values和nums数组,分别表示金额和数量,二者长度相等且一一对应int[] values = new int[count.size()];int[] nums = new int[count.size()];//根据count进行填充,从0下标开始填充int curIndex = 0;for (Integer value : count.keySet()) {//当前下标的value和num设置values[curIndex] = value;//这里要++,这样下次循环就可以进行下一个面值的统计了nums[curIndex ++] = count.get(value);}return new CoinsInfo(values, nums);}/*** 改进的递归-把coins按照面值进行分类,如果有很多重复的可以大大提高效率* @param values* @param nums* @param curIndex* @param restAmount* @return*/public static int process2(int[] values, int[] nums, int curIndex, int restAmount) {if(restAmount == 0) return 0;if(curIndex == values.length) return Integer.MAX_VALUE;//当前面值的钱可以用0个到nums[curIndex]个int ans = process2(values, nums, curIndex + 1, restAmount);for(int num = 1; num <= nums[curIndex]; num ++) {int nextMin = process2(values, nums, curIndex + 1, restAmount - num *values[curIndex]);if(nextMin != Integer.MAX_VALUE) {ans = Math.min(ans, nextMin) + num;}}return ans;}public static int coinChangeDp2(int[] coins, int amount) {if(amount == 0) return 0;CoinsInfo coinsInfo = getCoinsInfo(coins);int[] values = coinsInfo.values;int[] nums = coinsInfo.nums;int N = values.length;//动态规划数组int[][] dp = new int[N + 1][amount + 1];/*if(restAmount == 0) return 0;if(curIndex == values.length) return Integer.MAX_VALUE;*///根据递归中的上面两个if初始化动态规划数组, int默认是0所以第一个if忽略for(int j = 1; j <= amount; j++) {dp[N][j] = Integer.MAX_VALUE;}//对于普遍位置进行赋值for(int curIndex = N - 1; curIndex >= 0; curIndex --) {for(int restAmount = 0; restAmount <= amount; restAmount ++) {//当前面值的钱可以用0个到nums[curIndex]个int ans = dp[curIndex + 1][restAmount];for(int num = 1; num <= nums[curIndex]; num ++) {int nextMin = dp[curIndex + 1][restAmount - num *values[curIndex]];if(nextMin != Integer.MAX_VALUE) {ans = Math.min(ans, nextMin) + num;}}//斜率优化的相关分析,假设values数组为[1,2,3,5] nums为[4,2,2,1], amount = 15;//那我们推算dp[1][8] = dp[2][8] dp[2][6] + 1 dp[2][4] + 2中取最小值//而dp[1][6] = dp[2][6] dp[2][4] + 1 dp[2][2] + 2中的最小值//此时的dp[1][8]并不能根据dp[1][6]和dp[2][8]计算出,因为dp[1][6]用到的dp[2][2]+2在dp[1][8]中并没有,而这个可能就是最小值}}int result = dp[0] [amount];return result == Integer.MAX_VALUE? -1 : result;}public static int coinChangeSlideWindow(int[] coins, int aim) {if(aim == 0) return 0;CoinsInfo coinsInfo = getCoinsInfo(coins);int[] values = coinsInfo.values;int[] nums = coinsInfo.nums;int N = values.length;//动态规划数组int[][] dp = new int[N + 1][aim + 1];/*if(restAmount == 0) return 0;if(curIndex == values.length) return Integer.MAX_VALUE;*///根据递归中的上面两个if初始化动态规划数组, int默认是0所以第一个if忽略for(int j = 1; j <= aim; j++) {dp[N][j] = Integer.MAX_VALUE;}//对于普遍位置进行赋值for(int curIndex = N - 1; curIndex >= 0; curIndex --) {//使用窗口内最小值的更新结构,对于每个面值的货币尝试的方位是0~(目标金额和货币金额达的最小值-1)//比如如果面值是30,而目标值是100,那我们进行以下的尝试:(0 30 60 90) (1 31, 61, 91) ...(29, 59, 89)//而对于面值100,目标值30,我们进行:(0) (1)...(29)这些尝试//mod代表我们当前准备尝试多少组,例如上面的面值是30,而目标值是100,我们mod就从0到29,一共30组for(int mod = 0; mod < Math.min(aim+1, coins[curIndex]); mod ++) {//创建一个最小值窗口,放的是当前的金额LinkedList<Integer> window = new LinkedList<>();//mod是当前组的第一个位置,先进去,暂时是窗口最小值window.add(mod);//先把dp[curIndex][mod]的初始值设置为dp[curIndex + 1][mod]dp[curIndex][mod] = dp[curIndex + 1][mod];//在小于目标金额的情况下,每次加当前面值,类似0 30 60 90,不超过目标金额for(int curAmount = mod + coins[curIndex]; curAmount <= aim; curAmount += coins[curIndex]) {//如果窗口不为空且窗口最后的数是Integer的最大值或者最后的值+补偿金额大于当前的数量(下一行的dp值)while(!window.isEmpty() && (dp[curIndex+1][window.peekLast()] == Integer.MAX_VALUE || window.peekLast() + composite(window.peekLast(), curAmount, coins[curIndex]) >= dp[curIndex + 1][curAmount])) {window.pollLast();}//把当前金额(dp里的列)window.addLast(curAmount);//当前钱数-货币金额*货币数量刚好是可用的,如果再加一个就不行了,计算当前值或者计算下一个值的时候就都不能用了int overdue = curAmount - coins[curIndex] * (nums[curIndex] + 1);if(window.peekFirst() == overdue) {window.pollFirst();}dp[curIndex][curAmount] = dp[curIndex + 1][window.peekFirst()] + composite(window.peekFirst(), curAmount, coins[curIndex]);}//斜率优化的相关分析,假设values数组为[1,2,3,5] nums为[4,2,2,1], amount = 15;//那我们推算dp[1][8] = dp[2][8] dp[2][6] + 1 dp[2][4] + 2中取最小值//而dp[1][6] = dp[2][6] dp[2][4] + 1 dp[2][2] + 2中的最小值//此时的dp[1][8]并不能根据dp[1][6]和dp[2][8]计算出,因为dp[1][6]用到的dp[2][2]+2在dp[1][8]中并没有,而这个可能就是最小值}}int result = dp[0] [aim];return result == Integer.MAX_VALUE? -1 : result;}private static int composite(int preAmount, int curAmount, int coinAmount) {return (curAmount - preAmount)/ coinAmount;}public static void main(String[] args) {int[] coins = {2,5,3,2,5,2};int amount = 15;int nums = coinChange(coins, amount);System.out.println(nums);int numsDp = coinChangeDp(coins, amount);System.out.println(numsDp);int nums2 = coinChange2(coins, amount);System.out.println(nums2);int numsDp2 = coinChange2(coins, amount);System.out.println(numsDp2);int numsWindow = coinChangeSlideWindow(coins, amount);System.out.println(numsWindow);}
}class CoinsInfo {int[] values;int[] nums;public CoinsInfo(int[] values, int[] nums) {this.values = values;this.nums = nums;}
}

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

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

相关文章

用NeRFMeshing精确提取NeRF网络中的3D网格

准确的 3D 场景和对象重建对于机器人、摄影测量和 AR/VR 等各种应用至关重要。 NeRF 在合成新颖视图方面取得了成功&#xff0c;但在准确表示底层几何方面存在不足。 推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 我们已经看到了最新的进展&#xff0c;例如 NVIDIA 的…

【c++】VC编译出的版本,发布版本如何使用

目录 使用release类型进行发布 应用程序无法正常启动 0xc000007b 版本对应 vcruntime140d 应用版本 参考文章 使用release类型进行发布 应用程序无法正常启动 0xc000007b "应用程序无法正常启动 0xc000007b" 错误通常是一个 Windows 应用程序错误&#xf…

【网络】多路转接——poll | epoll

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《网络》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 书接上文五种IO模型 | select。 poll | epoll &#x1f367;poll&#x1f9c1;认识接口&#x1f9c1;简…

多目标应用:基于多目标向日葵优化算法(MOSFO)的微电网多目标优化调度MATLAB

一、微网系统运行优化模型 参考文献&#xff1a; [1]李兴莘,张靖,何宇,等.基于改进粒子群算法的微电网多目标优化调度[J].电力科学与工程, 2021, 37(3):7 二、多目标向日葵优化算法 多目标向日葵优化算法&#xff08;Multi-objective sunflower optimization&#xff0c;MOS…

idea远程debug调试

背景 有时候我们线上/测试环境出现了问题&#xff0c;我们本地跑却无法复现问题&#xff0c;使用idea的远程debug功能可以很好的解决该问题 配置 远程debug的服务&#xff0c;我们使用Springboot项目为例(SpringCloud作为微服务项目我们可以可以使用本地注册到远程项目&…

Android中级——消息机制

消息机制 概念ThreadLocalMessageQueueLooperHandlerrunOnUiThread() 概念 MessageQueue&#xff1a;采用单链表的方法存储消息列表Looper&#xff1a;查询MessageQueue是否有新消息&#xff0c;有则处理&#xff0c;无则等待ThreadLocal&#xff1a;用于Handler获取当前线程的…

NSS [羊城杯 2020]easyser

NSS [羊城杯 2020]easyser 开题。很容易让人觉得环境坏了。 不要慌&#xff0c;无从下手时。看源码、扫目录、抓包。一套操作下来&#xff0c;发现几个可以下手的路由。 /index.php /robots.txt 访问 /star1.php&#xff0c;一说到百度&#xff0c;就猜测是否存在SSRF。 源码中…

【ES6】JavaScript 中的数组方法reduce

reduce() 是一个 JavaScript 中的数组方法&#xff0c;它会对数组的每个元素执行一个提供的 reducer 函数&#xff0c;将其减少到一个单一的值。 这是 reduce() 的基本用法&#xff1a; //(method) Array<number>.reduce(callbackfn: (previousValue: number, currentV…

<C++> STL_list

1.list的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向 其前一个元素和后一个元素。list与…

Quickstart: MinIO for Linux

单节点部署教程 1.安装Minio服务端 //wget下载二进制文件 wget https://dl.min.io/server/minio/release/linux-amd64/minio //赋予权限 chmod x minio //将minio可执行文件移入usr/local/bin目录下&#xff0c;使得minio可以全局执行 sudo mv minio /usr/local/bin/ 2.启动Mi…

多线程学习之解决线程同步的实现方法

一、卖票的多线程实现 需求&#xff1a;共有100张票&#xff0c;而它有3个窗口卖票&#xff0c;请设计一个程序模拟该电影院卖票 代码实现&#xff1a; /*** Author&#xff1a;kkoneone11* name&#xff1a;SellTicket1* Date&#xff1a;2023/8/26 11:32*/ public class S…

电子电路学习笔记之NCP304LSQ37T1G ——超低电流电压检测器

超低电流电压检测器是一种专门用于检测极小电流值的设备。它们常用于电子元件或电路中&#xff0c;用于监测电流的存在和程度。这些检测器通常具有高灵敏度和高精度&#xff0c;能够测量微安级别或更小的电流。 超低电流电压检测器的应用领域广泛&#xff0c;例如电池管理系统…

vue实现自定义树形组件

欢迎点击关注-前端面试进阶指南&#xff1a;前端登顶之巅-最全面的前端知识点梳理总结 *分享一个使用比较久的&#x1fa9c; 效果展示&#xff1a; 近期的一个功能需求&#xff0c;实现一个树形结构&#xff1a;可点击&#xff0c;可拖拽&#xff0c;右侧数据可以拖拽到对应的…

六、Json 数据的交互处理

文章目录 一、JSON 数据的交互处理1、为什么要使用 JSON2、JSON 和 JavaScript 之间的关系3、前端操作 JSON3.1 JavaScript 对象与 JSON 字符串之间的相互转换 4、JAVA 操作 JSON4.1 Json 的解析工具&#xff08;Gson、FastJson、Jackson&#xff09;4.2 ResponseBody 注解、Re…

Fedora Linux 的家族(一):官方版本

导读本文将对 Fedora Linux 官方版本进行更详细的介绍。共有五个 版本&#xff1a; Fedora Workstation、Fedora Server、Fedora IoT、Fedora CoreOS 和 Fedora Silverblue。Fedora Linux 下载页面目前显示其中三个为 官方 版本&#xff0c;另外两个为 新兴 版本。本文将涵盖所…

LOIC(low orbit ion cannon)

前言 重要的话说三遍&#xff1a; 该程序仅用于学习用途&#xff0c;请勿用于非法行为上&#xff01;&#xff01;&#xff01; 该程序仅用于学习用途&#xff0c;请勿用于非法行为上&#xff01;&#xff01;&#xff01; 该程序仅用于学习用途&#xff0c;请勿用于非法行为上…

【C++初阶】list的常见使用操作

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

关闭jenkins插件提醒信息

jenkins提醒信息和安全警告可以帮助我们了解插件或者jenkins的更新情况&#xff0c;但是有些插件是已经不维护了&#xff0c;提醒却一直存在&#xff0c;看着糟心&#xff0c;就像下面的提示 1、关闭插件提醒 找到如下位置&#xff1a;系统管理-系统配置-管理监控配置 打开管…

线性代数的学习和整理11: 子式与余子式

目录 1 原始矩阵A 2 子式&#xff08;都是行列式&#xff09; 2.1 k阶子式&#xff08;行数列数即可&#xff09; 比如1阶子式&#xff1a;因为只有1行1列 比如2阶子式&#xff1a;因为有2行2列 比如3阶子式&#xff1a;因为有3行3列 2.2 k阶主子式 {行序号数组} {列序号…

java对时间序列根据阈值进行连续性分片

问题描述&#xff1a;我需要对一个连续的时间戳list进行分片&#xff0c;分片规则是下一个数据比当前数据要大于某一个阈值则进行分片&#xff1b; 解决方式&#xff1a; 1、输入的有顺序的list &#xff0c;和需要进行分片的阈值 2、调用方法&#xff0c;填入该排序的list和阈…