算法32:针对算法31货币问题进行扩展,并对从左往右模型进行总结

本算法是在算法31的基础之上进行推理总结的,因此,在看本章之前,必须先去了解算法31,否则会觉得莫名其妙。

算法31的推理过程:

如果 x = y1 + y2 + y3 + y4 + y5 + y6.   x1 = y2 + y3 + y4 + y5 + y6

那么 x = y1 + x1.   

根据以上推导公式,可以对时间复杂度进行优化。

之前我们对从左往右模型进行过总结,即:

1. 针对固定集合,值不同,就是讨论要和不要的累加和。算法30有完整的例子

2. 针对非固定集合,面值固定,张数无限。口诀就是讨论要与不要,要的话逐步讨论要几张的累加和。算法31有完整的例子

今天,我们讨论最后一种情况,即:

3. 针对非固定集合,面值固定,张数随机。也就是说有可能只有0张,1张,2张,甚至也是无限的情况。口诀就是在口诀2的基础之上要去除多余项

题目:

arr是货币数组,其中的值都是正数。再给定一个正数aim。  每个值都认为是一张货币,  认为值相同的货币没有任何不同,  返回组成aim的方法数 。

 例如:arr = {1,2,1,1,2,1,2},aim = 4  

方法:1+1+1+1、1+1+2、2+2  一共就3种方法,所以返回3。

也就是说,所有的1都是面值相同的,没有什么区别。

举个例子:给你6个1毛钱硬币,一个5毛钱硬币,要求你列举出能凑成1元钱的组合。答案肯定是1种呀,你不可能说每个1毛钱都是不一样,6个一毛轮流拿掉一个,剩余的和5毛钱组合,总共有5种组合方法吧。

下面说一下今天的推导公式。

假设某一行的value为3, 张数为2,aim还是15.  

那么基于算法31,我们可以对算法32进行假设:

是不是会有人问, 算法31不是会把y4,y5,y6等等情况都列举出来的吗,为什么本章算法就假设了value为3,张数只为2的情况呢。因为本算法每个面值的张数是不固定的,随机的。如果张数足够多,那逻辑就和算法31一样了。

思路:

1. 首先,我们需要对数组的每个面值以及对应的张数进行统计

2. 在讨论要不要,以及要几张的时候,需要在算法31的基础之上考虑实际可能存在的张数。

递归代码:

static class Info {int[] value;int[] zhangshu;Info (int[] k, int[] v) {value = k;zhangshu = v;}}public static Info getInfo (int[] arr) {Map map = new HashMap<Integer, Integer>();for (int i = 0; i < arr.length; i++) {int v = arr[i];if (map.get(v) != null) {map.put(v, (int) map.get(v) + 1);}else {map.put(v, 1);}}int[] k = new int[map.size()];int[] v = new int[map.size()];int index = 0;for (Iterator iterator = map.keySet().iterator(); iterator.hasNext();) {int key = (int) iterator.next();int value = (int) map.get(key);k[index] = key;v[index++] = value;}return new Info(k, v);}public static int ways(int[] arr, int aim){if (arr == null || arr.length == 0 || aim < 0) {return 0;}Info info = getInfo(arr);return process(info.value, info.zhangshu, 0, aim);}public static int process (int[] value, int[] zhangshu, int index, int aim){//面值数组结束了if (index == value.length) {return aim == 0 ? 1 : 0;}int ways = 0;for (int zhang = 0; zhang <= zhangshu[index] && zhang * value[index] <= aim; zhang++) {ways += process(value, zhangshu, index+1, aim- zhang * value[index]);}return ways;}

动态规划版本:

//动态规划public static int ways2(int[] arr, int aim){if (arr == null || arr.length == 0 || aim < 0) {return 0;}Info info = getInfo(arr);//数组值int[] value = info.value;//每个值对应的张数int[] zhangshu = info.zhangshu;int[][] dp = new int[value.length + 1][aim + 1];//最后一行的初始值dp[value.length][0] = 1;//数组值为行for (int row =  value.length - 1; row >= 0; row--) {//aim为列for (int col = 0; col <= aim; col++) {int ways = 0;for (int zhang = 0; zhang * value[row] <= col && zhang <= zhangshu[row]; zhang++) {ways += dp[row + 1][col - (zhang * value[row])];}dp[row][col] = ways;}}return dp[0][aim];}

对动态规划进行时间复杂度优化

//动态规划 + 时间复杂度public static int ways3(int[] arr, int aim){if (arr == null || arr.length == 0 || aim < 0) {return 0;}Info info = getInfo(arr);//数组值int[] value = info.value;//每个值对应的张数int[] zhangshu = info.zhangshu;int[][] dp = new int[value.length + 1][aim + 1];//最后一行的初始值dp[value.length][0] = 1;//数组值为行for (int row =  value.length - 1; row >= 0; row--) {//aim为列for (int col = 0; col <= aim; col++) {/*** 此处的代码,就是 x = x1 + y1.* 即包含了多余的值了*/dp[row][col] = dp[row + 1][col];if (col - value[row] >= 0) {dp[row][col] += dp[row][col - value[row]];}/*** row代表推理的value, col代表列的下标,即代表aim的值** 如果col就是我们想要的值,那么我们必须根据张数往前找。* 如果value为3,张数为2,col为15,* 那么我们就应该得到下一行的列下标为 15, 12, 9的值。而* 多余的下标就是下一行列为6的值。** value数组代表面值不同的数组: 此处的value[row] = 3.* zhangshu数组代表当前面值为3的张数。此处zhangshu[row] = 2.* 那么多余的位置不就是:* 15 - 3 *(2+1) = 6 吗?*/if (col - value[row] * (zhangshu[row] + 1) >= 0) {//既然是多余的,那当然要减去多余的推导了。dp[row][col] -= dp[row + 1][col - value[row] * (zhangshu[row] + 1)];}}}return dp[0][aim];}

完整代码以及添加对数器进行测试

package code03.动态规划_07.lesson4;import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;/*** arr是货币数组,其中的值都是正数。再给定一个正数aim。* 每个值都认为是一张货币,* 认为值相同的货币没有任何不同,* 返回组成aim的方法数* 例如:arr = {1,2,1,1,2,1,2},aim = 4* 方法:1+1+1+1、1+1+2、2+2* 一共就3种方法,所以返回3*/
public class ContainWaysLimitCountPaper_06 {static class Info {int[] value;int[] zhangshu;Info (int[] k, int[] v) {value = k;zhangshu = v;}}public static Info getInfo (int[] arr) {Map map = new HashMap<Integer, Integer>();for (int i = 0; i < arr.length; i++) {int v = arr[i];if (map.get(v) != null) {map.put(v, (int) map.get(v) + 1);}else {map.put(v, 1);}}int[] k = new int[map.size()];int[] v = new int[map.size()];int index = 0;for (Iterator iterator = map.keySet().iterator(); iterator.hasNext();) {int key = (int) iterator.next();int value = (int) map.get(key);k[index] = key;v[index++] = value;}return new Info(k, v);}public static int ways(int[] arr, int aim){if (arr == null || arr.length == 0 || aim < 0) {return 0;}Info info = getInfo(arr);return process(info.value, info.zhangshu, 0, aim);}public static int process (int[] value, int[] zhangshu, int index, int aim){//面值数组结束了if (index == value.length) {return aim == 0 ? 1 : 0;}int ways = 0;for (int zhang = 0; zhang <= zhangshu[index] && zhang * value[index] <= aim; zhang++) {ways += process(value, zhangshu, index+1, aim- zhang * value[index]);}return ways;}//动态规划public static int ways2(int[] arr, int aim){if (arr == null || arr.length == 0 || aim < 0) {return 0;}Info info = getInfo(arr);//数组值int[] value = info.value;//每个值对应的张数int[] zhangshu = info.zhangshu;int[][] dp = new int[value.length + 1][aim + 1];//最后一行的初始值dp[value.length][0] = 1;//数组值为行for (int row =  value.length - 1; row >= 0; row--) {//aim为列for (int col = 0; col <= aim; col++) {int ways = 0;for (int zhang = 0; zhang * value[row] <= col && zhang <= zhangshu[row]; zhang++) {ways += dp[row + 1][col - (zhang * value[row])];}dp[row][col] = ways;}}return dp[0][aim];}//动态规划 + 时间复杂度public static int ways3(int[] arr, int aim){if (arr == null || arr.length == 0 || aim < 0) {return 0;}Info info = getInfo(arr);//数组值int[] value = info.value;//每个值对应的张数int[] zhangshu = info.zhangshu;int[][] dp = new int[value.length + 1][aim + 1];//最后一行的初始值dp[value.length][0] = 1;//数组值为行for (int row =  value.length - 1; row >= 0; row--) {//aim为列for (int col = 0; col <= aim; col++) {/*** 此处的代码,就是 x = x1 + y1.* 即包含了多余的值了*/dp[row][col] = dp[row + 1][col];if (col - value[row] >= 0) {dp[row][col] += dp[row][col - value[row]];}/*** row代表推理的value, col代表列的下标,即代表aim的值** 如果col就是我们想要的值,那么我们必须根据张数往前找。* 如果value为3,张数为2,col为15,* 那么我们就应该得到下一行的列下标为 15, 12, 9的值。而* 多余的下标就是下一行列为6的值。** value数组代表面值不同的数组: 此处的value[row] = 3.* zhangshu数组代表当前面值为3的张数。此处zhangshu[row] = 2.* 那么多余的位置不就是:* 15 - 3 *(2+1) = 6 吗?*/if (col - value[row] * (zhangshu[row] + 1) >= 0) {//既然是多余的,那当然要减去多余的推导了。dp[row][col] -= dp[row + 1][col - value[row] * (zhangshu[row] + 1)];}}}return dp[0][aim];}// 为了测试public static int[] randomArray(int maxLen, int maxValue) {int N = (int) (Math.random() * maxLen);int[] arr = new int[N];for (int i = 0; i < N; i++) {arr[i] = (int) (Math.random() * maxValue) + 1;}return arr;}// 为了测试public static void printArray(int[] arr) {for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}public static void main(String[] args) {/*  int[] arr = {1,2,1,1,2,1,2};int aim = 4;System.out.println(ways(arr, aim));System.out.println(ways2(arr, aim));*/int maxLen = 10;int maxValue = 20;int testTime = 1000000;System.out.println("测试开始");for (int i = 0; i < testTime; i++) {int[] arr = randomArray(maxLen, maxValue);int aim = (int) (Math.random() * maxValue);int ans1 = ways(arr, aim);int ans2 = ways2(arr, aim);if (ans1 != ans2) {System.out.println("Oops!");printArray(arr);System.out.println(aim);System.out.println(ans1);System.out.println(ans2);break;}}System.out.println("测试结束");}
}

至于空间复杂度优化,可以参考算法31进行研究,看看此题是否还可以对空间复杂度进行优化

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

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

相关文章

Codeforces Round 911 C. Anji‘s Binary Tree

原题&#xff1a; C. Anji’s Binary Tree time limit per test 2.5 seconds memory limit per test 256 megabytes input standard input output standard output Keksic keeps getting left on seen by Anji. Through a mutual friend, he’s figured out that Anji really …

ECharts 图表简单示例,中国地图

目录 ECharts官网链接: [ECharts](https://echarts.apache.org/zh/index.html)在项目中引入 Apache ECharts柱状图折线图饼图仪表盘中国地图完整示例代码 ECharts官网链接: ECharts 在项目中引入 Apache ECharts <!DOCTYPE html> <html><head><meta char…

JavaWeb——后端AOP面向特定方法编程

七、AOP 1. 概述 AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;面向切面编程、面向方法编程&#xff0c;其实就是面向特定方法编程 场景&#xff1a; 案例部分功能运行较慢&#xff0c;定位执行耗时较长的业务方法&#xff0c;此时需要统计每个业务…

2024年数学建模美赛能用chatGPT之类的AI吗?官方给了明确规定!

这两年chatGPT等大语言模型火了&#xff0c;能对话&#xff0c;自然也能回答数学建模方面的问题。 那美赛能不能用这些AI呢&#xff1f;2024年美赛官方对chatGPT等的使用做出了明确的规定&#xff08;其中的VI. Contest Instructions部分&#xff09;&#xff1a; https://ww…

JavaScript高级程序设计读书记录(六):定型数组,Map

1. 定型数组 定型数组&#xff08;typed array&#xff09;是 ECMAScript 新增的结构&#xff0c;目的是提升向原生库传输数据的效率。实际上&#xff0c;JavaScript 并没有“TypedArray”类型&#xff0c;它所指的其实是一种特殊的包含数值类型的数组。 1.1 历史 随着浏览器…

LaTex引用字体变色

使用下面这条语句进行修改。 ‘citecolor’改变参考文献颜色&#xff0c; ‘linkcolor’改变图标公式引用的颜色&#xff0c; ‘urlcolor’ 文本网站超链接颜色。 \usepackage[colorlinks,bookmarksopen,bookmarksnumbered,citecolorblue, linkcolorblue, urlcolorblue]{hyper…

杨中科 ASP.NET Core前后端分离开发

一、 前后端分离 1、传统MVC开发模式: 前后端的代码被放到同一个项目中&#xff0c;前端人员负责编写页面的模板&#xff0c;而后端开发人员负责编写控制器和模型的代码并且“套模板”。 缺点: 互相依赖&#xff0c;耦合性强&#xff0c;责任划分不清。 2、主流的“前后端分离…

【openGauss服务器端工具的使用】

【openGauss服务器端工具的使用】 gs_checkperf openGauss 不仅提供了gs_checkperf工具来帮助用户了解openGauss的负载情况。 使用数据库安装用户登录服务器&#xff0c;执行如下命令进行查看数据库性能&#xff1a; 简要信息展示&#xff1a;[ommopengauss03 ~]$ gs_checkperf…

跨平台的传输协议@WebDav协议@windows系统配置WedDav服务器@局域网内的WebDav传输系统

文章目录 WebDav协议基本信息启用必要的windows功能启动站点管理器IIS站点根目录访问权限设置站点的功能设置端口通行防火墙IMME文件类型(文件后缀)其他设备登录和访问本机的WebDav服务站点 小结优点缺点 refs WebDav 协议基本信息 来自wikipedia:基于Web的分布式编写和版本控…

协程池与新脚本语言

今天的主人公名为——Melang。 这是一款使用C语言开发的“新”的脚本语言&#xff0c;然而其已经默默问世了6年之久。 下面笔者就带你走进Melang world。 What is Melang Melang是一款协程并发脚本语言。它是一款解释型&#xff0c;而非编译型语言。 在Melang中&#xff…

Tmux 使用小记

本文参考自 阮一峰老师Tmux 使用教程[1] Tmux,不仅仅是分屏那么简单。。。 与tmux类似的工具是screen 会话管理 将窗口与会话"解绑" 对于没有图形界面只有shell的场景(如服务器)&#xff0c;尤其有用..这是其最核心解决的问题(窗口管理啥的只能算锦上添花的辅助功能)…

代码随想录算法训练营第20天 | 654.最大二叉树 617.合并二叉树 700.二叉搜索树中的搜索 98.验证二叉搜索树

目录 654.最大二叉树 &#x1f4a1;解题思路 &#x1f4bb;实现代码 617.合并二叉树 &#x1f4a1;解题思路 递归 &#x1f4bb;实现代码 700.二叉搜索树中的搜索 &#x1f4a1;解题思路 递归法 迭代法 &#x1f4bb;实现代码 98.验证二叉搜索树 &#x1f4a1;解题…

pod进阶版(1)

pod的相关知识 k8s的pad重启策略: Always deployment的yaml文件只能是Always pod的yaml三种模式都可以。 Onfailure:只有异常退出状态码非0才会重启。正常退出不重启。 Never&#xff1a;非正常退出和非正常退出都不重启。 容器的退出了pod才会重启。 pod可以有多个容器&…

spring Security源码讲解-WebSecurityConfigurerAdapter

使用security我们最常见的代码&#xff1a; Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().permitAll();http.authorizeRequests().antMatcher…

为什么我国的计算机教育那么差?

建议看看计算机科学速成课&#xff0c;一门很全面的计算机原理入门课程&#xff0c;短短10分钟可以把大学老师十几节课讲不清楚的东西讲清楚&#xff01;整个系列一共41个视频&#xff0c;B站上有中文字幕版。 每个视频都是一个特定的主题&#xff0c;例如软件工程、人工智能、…

顺序表实现(下)(C语言)

几道相关例题,帮助大家更好理解顺序表. 文章目录 前言 一、顺序表二、创建顺序表并初始化三.删除非递减顺序表L中的重复元素四.在非递减顺序表中删除[s,t]之间的元素五.设计算法逆置顺序表L,并将序列L循环左移六.顺序表A和B的元素个数分别为m,n.A表升序排序,B表降序排序,两表中…

AI变现项目:刚做五天收益突破单日破50+,干货经验谈

今日是我单号操作的第五天。 打开今日头条&#xff0c;发现收益破新高了。 我这是一个号操作&#xff0c;10个号&#xff0c;20个号呢&#xff1f; 下面主要说说我的操作经验。 先确定领域 我是做的情感故事领域。 为什么做这个领域&#xff1f;(简单&#xff0c;原创度高…

家用洗地机哪款好用?洗地机品牌排行榜推荐

在如今的日常生活中&#xff0c;家用洗地机已经成为了家庭清洁中不可或缺的工具。然而&#xff0c;市面上各种不同品牌型号的洗地机让人眼花缭乱&#xff0c;让人难以选择。那么&#xff0c;家用洗地机现在买什么牌子质量好呢?为了解答这个问题&#xff0c;笔者选了几款品牌质…

120°AGV|RGV小车激光障碍物传感器|避障雷达DE系列安装与连线方法

120AGV|RGV小车激光障碍物传感器|避障雷达DE系列包含DE-4211、DE-4611、DE-4311、DE-4511等型号&#xff0c;根据激光飞行时间&#xff08;TOF&#xff09;测量原理运行的&#xff0c;利用激光光束对周围进行 120 半径 4m&#xff08;90%反射率&#xff09;扫描&#xff0c;获得…

鸿蒙开发解决agconnect sdk not initialized. please call initialize()

文章目录 项目场景:问题描述原因分析:解决方案:总结:项目场景: 鸿蒙开发报错: agconnect sdk not initialized. please call initialize() 问题描述 报错内容为: 10-25 11:41:01.152 6076-16676 E A0c0d0/JSApp: app Log: 数据查询失败: {“code”:1100001,“messag…