算法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.rhkb.cn/news/235866.html

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

相关文章

探索 TCP 与 UDP:网络通信的两门学派(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

python学习笔记9(程序的描述方式、程序的组织结构、顺序结构、选择结构1)

&#xff08;一&#xff09;程序的描述方式 自然语言、流程图、伪代码 &#xff08;二&#xff09;程序的组织结构 顺序、选择、循环 &#xff08;三&#xff09;顺序结构 &#xff08;四&#xff09;选择结构1 if 1、条件写法1 2、如果只有一个判断的写法 3、注意冒号和缩进…

网络编程的理论基础

文章目录 1 重点知识2 应用层3 再谈 "协议"4 HTTP协议4.1 认识URL4.2 urlencode和urldecode4.3 HTTP协议格式4.4 HTTP的方法4.5 HTTP的状态码4.6 HTTP常见Header4.7 最简单的HTTP服务器 3 传输层4 再谈端口号4.1 端口号范围划分4.2 认识知名端口号(Well-Know Port Nu…

用通俗易懂的方式讲解大模型分布式训练并行技术:自动并行

之前的文章已经对多种并行技术进行了讲解&#xff0c;如&#xff1a;数据并行、张量并行、流水线并行以及多种并行方式组合使用的多维混合并行。 然而大模型的分布式训练是一个非常复杂的问题&#xff0c;目前的绝大多数的分布式训练系统&#xff0c;都依赖用户人工反复尝试以…

基于Pixhawk和ROS搭建自主无人车(一):底盘控制篇

参考 ArduPilot Development超维空间科技乐迪MiniPix车船使用说明书 1. 硬件篇 1.1 底盘构成一览 1.2 底盘接线示意 2. 软件篇 2.1 APM 固件下载 pixhawk 是硬件平台&#xff0c;PX4 是 pixhawk 的原生固件&#xff0c;APM&#xff08;Ardupilot Mega&#xff09;是硬件平台…

Object.keys()

目录 1、Object.keys() 是什么&#xff1f; 2、Object.keys(obj) 用法&#xff1a; 2.1 如果对象是一个对象&#xff0c;会返回对象的属性名组成的数组&#xff1b; 2.2 如果对象是一个数组&#xff0c;则返回索引组成的数组&#xff1a; 2.3 如果是字符串&#xff0c;返回…

AI系统ChatGPT网站系统源码AI绘画详细搭建部署教程,支持GPT语音对话+DALL-E3文生图+GPT-4多模态模型识图理解

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

基于JavaWeb+BS架构+SpringBoot+Vue+Hadoop短视频流量数据分析与可视化系统的设计和实现

基于JavaWebBS架构SpringBootVueHadoop短视频流量数据分析与可视化系统的设计和实现 文末获取源码Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 文末获取源码 Lun文目录 目  录 目  录 I 1绪 论 1 1.1开发背景 1 1.2开…

SpringCloud 之HttpClient、HttpURLConnection、OkHttpClient切换源码

SpringCloud 之HttpClient、HttpURLConnection、OkHttpClient切换源码 HttpClient、HttpURLConnection、OkHttpClient区别切换HttpClient 源码分析总结切换HttpClient源码验证切换是否成功okHttpClient 切换源码分析总结 okHttpClient 切换源码同时开启 okHttp 与httpClient 会…

Sqoop入门指南:安装和配置

Sqoop是一个强大的工具&#xff0c;用于在Hadoop和关系型数据库之间高效传输数据。在本篇文章中&#xff0c;将深入探讨如何安装和配置Sqoop&#xff0c;以及提供详细的示例代码。 安装Java和Hadoop 在开始安装Sqoop之前&#xff0c;首先确保已经成功安装了Java和Hadoop。Sqo…

Netty通信中的粘包半包问题(一)

前言 我们在日常开发过程中&#xff0c;客户端和服务端的连接大多使用的是TCP协议,因为我们要保证数据的可靠传输&#xff0c; 当网络中出现丢包时要求&#xff0c;要求数据包的发送端重传给接收端。而TCP是一种面向连接的传输层协议&#xff0c; 当使用TCP进行传输时&#xf…

【软件测试】学习笔记-设计GUI自动化测试策略

这篇文章从“实战”这个角度展开&#xff0c;探讨实际的大型全球化电商网站的GUI自动化测试如何开展。这场实战&#xff0c;从以下两个方面展开&#xff1a; 测试策略如何设计&#xff1f;这一点&#xff0c;我会根据亲身经历的实际项目&#xff0c;和你探讨GUI测试的分层测试…

使用CLIP和LLM构建多模态RAG系统

在本文中我们将探讨使用开源大型语言多模态模型(Large Language Multi-Modal)构建检索增强生成(RAG)系统。本文的重点是在不依赖LangChain或LLlama index的情况下实现这一目标&#xff0c;这样可以避免更多的框架依赖。 什么是RAG 在人工智能领域&#xff0c;检索增强生成(re…

每日一题——LeetCode1103.分糖果 ||

方法一 个人方法&#xff1a; 有多少人就创建多大的数组并把数组的所有元素初始化为0&#xff0c;只要还有糖果&#xff0c;就循环给数组从头到尾添加糖果&#xff0c;每次分的糖果数递增1&#xff0c;最后可能刚好分完也可能不够&#xff0c;不够就还剩多少给多少。 var dis…

作业--day45

定时播放 #include "mywidget.h" #include "ui_mywidget.h"MyWidget::MyWidget(QWidget *parent) :QWidget(parent),ui(new Ui::MyWidget) {ui->setupUi(this);ui->bg_lab->setPixmap(QPixmap(":/pictrue/shanChuan.jpg"));ui->bg_…

Leetcode2981. 找出出现至少三次的最长特殊子字符串 I

Every day a Leetcode 题目来源&#xff1a;2981. 找出出现至少三次的最长特殊子字符串 I 解法1&#xff1a;滑动窗口 暴力枚举 滑动窗口枚举窗口内字符相同的字符串&#xff0c;再暴力枚举长度相等的字符串。 代码&#xff1a; /** lc appleetcode.cn id2981 langcpp**…

国标28181平台的手机视频监控客户端的电子地图功能对比

目 录 一、手机客户端 1、概述 2、具体功能简述 二、电子地图功能 1、经纬度定位 2、附近设备 3、实时浏览功能 4、录像回放 5、缩放功能 三、手机web客户端和CS客户端上的电子地图功能对比 1、对比表 2、测距&#xff08;PC客户端功能&#xff09; 3…

精品公式——“V型反转”,精准把握V型反转行情,主副图分享

► 日线表现 代码评估 技术指标代码评估&#xff1a; M5, M14, M25 - 指数移动平均线&#xff08;EMA&#xff09;: M5:EMA(C,5),COLORLIBLUE;&#xff1a;5日指数移动平均线&#xff0c;用浅蓝色表示。 M14:EMA(C,13),COLORF00FF0;&#xff1a;13日指数移动平均线&#xff…

OpenHarmony—开发环境搭建

背景 因为没有实体的开发硬件&#xff0c;且不想破坏原有的Linux环境&#xff0c;所以这里基于 Docker QEMU 搭建开发环境 宿主机Linux系统命令行方式DockerQEMU 6.2 Docker环境准备 安装Docker 在Ubuntu中&#xff0c;可以使用下面的命令来安装Docker&#xff1a; sudo …

【软件测试】学习笔记-从0到1:API测试怎么做

这篇文章是API测试的基础&#xff0c;先从0到1设计一个API测试用例&#xff0c;通过这个测试用例&#xff0c;体会到最基本的API测试是如何进行的&#xff0c;并介绍几款常用的API测试工具。 API测试的基本步骤 通常来讲&#xff0c;无论采用什么API测试工具&#xff0c;API测…