解决动态规划问题4步曲

概述

  1. (确定状态)确定问题状态
    • 提炼最后一步
    • 子问题转化
  2. (求得方程)转移方程,把问题方程化
  3. (设初置界)按照实际逻辑设置初始条件和边界情况
  4. (确序再解)确定计算顺序并求解

一个案例:最少硬币组合

你打算买一本27元的书,你现有三种硬币,分别面值2元,5元和7元,每种硬币都充足。请问如何用个数最少的硬币组合正好付清?

正常人第一反应思路:

最少硬币组合?

  • 优先使用大面值硬币 —— 7+7+7+5=26,不符合求解目标27元。
  • 换种组合 —— 7+7+7+2+2+2=27,总共用了6枚硬币正好27元。
  • 实际正确答案 —— 7+5+5+5+5=27,才用了5枚硬币。

所以这里贪心算法是并不适用。

题目中关键词“最少的”,这是用到“动态规划”的味道

解决动态规划问题4步

第一步,确定问题状态

动态规划问题求解需要先开一个数组,并确定数组的每个元素f[i]代表什么,这就是确定这个问题的状态。这类似于解数学题中,设定x,y,z代表什么。

A、确定状态首先提取“最后一步”

最优策略必定是K枚硬币a1, a2, …, aK面值加起来是27。

找出不影响最优策略的最后一个独立角色,这道问题中,那枚最后的硬币aK就是最后一步。把aK提取出来,硬币aK之前的所有硬币面值加总是27-aK。因为总体求最硬币数量最少策略,所以拼出27-aK的硬币数也一定最少(重要设定)。

在这里插入图片描述

B、转化子问题

最后一步aK提出来之后,我们只要求出“最少用多少枚硬币可以拼出27- aK”就可以了。

这种与原问题内核一致,但是规模变小的问题,叫做子问题

为简化定义,我们设状态f(X)=最少用多少枚硬币拼出总面值X

我们目前还不知道最后的硬币aK面额多少,但它的面额一定只可能是{2, 5, 7}之一。

  • 如果aK是2,f(27)应该是f(27-2) + 1(加上最后这一枚面值2的硬币)
  • 如果aK是5,f(27)应该是f(27-5) + 1(加上最后这一枚面值5的硬币)
  • 如果aK是7,f(27)应该是f(27-7) + 1(加上最后这一枚面值7的硬币)

除此以外,没有其他的可能了。至此,通过找到原问题最后一步,并将其转化为子问题。

为求面值总额27的最小的硬币组合数的状态就形成了,用以下函数表示:

f(27) = min{f(27-2)+1, f(27-5)+1, f(27-7)+1}

在这里插入图片描述

第二步,转移方程,把问题方程化

f[X] = min{f[X-2]+1, f[X-5]+1, f[X-7]+1}

动态规划都是要开数组,所以上面式子改用方括号表示。实际求解动态规划类问题,正确列出转移方程正确基本上就解决一半了。

递归的解法:

// f(X)返回最少用多少枚硬币拼出X
int f(int X) {// 0元钱只要0枚硬币if (X == 0) return 0;// 初始化用无穷大(int res = Integer.MAX_VALUE;// 最后一枚硬币是2元if (X >= 2) {res = Math.min(f(X – 2) + 1, res);}// 最后一枚硬币是5元if (X >= 5) {res =  Math.min(f(X – 5) + 1, res);}// 最后一枚硬币是7元if (X >= 7) {      res =  Math.min(f(X – 7) + 1, res);}return res;
}

执行图如下:

在这里插入图片描述

要算f(27),就要递归f(25)、f(22)、f(20),然后下边依次递归。

在这里插入图片描述

问题明显:重复递归太多

这是求f(27),等待时间还可以接受。如果求f(100)呢?

求总体最值,可优先考虑动态规划,不要贸然去递归。

第三步,按照实际逻辑设置边界情况和初始条件。

如果不按照实际逻辑设置边界情况和初始条件,即使转移方程正确也大概率无法跑通代码。

f[X] = min{f[X-2]+1, f[X-5]+1, f[X-7]+1}的边界情况是[x-2][x-5][x-7]不能小于0(硬币面值为正),也不能高于27。

故对边界情况设定如下

如果硬币面值不能组合出Y,就定义f[Y]=正无穷。例如,f[-1] = f[-2] = … = 正无穷f[1] = min{f[-1]+1, f[-4]+1,f[-6]+1} = 正无穷特殊情况:本题的F[0]对应的情况为F[-2]、F[-5]、F[-7],按照上文的边界情况设定结果是正无穷,但是实际上F[0]的结果是存在的(即使用0个硬币的情况下),F[0]=0

可是按照我们刚刚的设定,F[0]=F[0-2]+1= F[-2]+1=正无穷岂不是矛盾?这种用转移方程无法计算,但是又实际存在的情况,就必须通过手动定义。这里手动强制定义初始条件为:F[0]=0

而从0之后的数值是没矛盾的,比如F[1]= F[1-2]+1= F[-1]+1=正无穷(正无穷加任何数结果还是正无穷),F[2] = F[2-2]+1 = F[0]+1=1

第四步,确定计算顺序并计算求解

那么开始计算时,是从F[1]F[2]开始呢?还是从F[27]F[26]开始呢?

判断计算顺序正确与否的原则是:当我们要计算F[X](等式左边,如F[10])的时候,等式右边(f[X-2]f[X-5]f[X-7]等)都是已经得到结果的状态,这个计算顺序就是OK的。

实际就是从小到大的计算方式(偶有例外的情况)。

例如我们算到F[12]的时候,发现F[11]F[10]F[9]都已经算过了,这种算法就是对的。而开始算F[27]的时候,发现F[26]还没有算,这样的顺序就是错的。

很显然这样的情况下写一个for循环就够了。

回到这道题,采用动态规划的算法,每一步只尝试三种硬币,一共进行了27步。算法时间复杂度(即需要进行的步数)为27*3。

与递归相比,没有任何重复计算

本文的题目来源:LeetCode - Medium - 322. Coin Change

代码如下:

public class CoinChange {public int coinChange(int[] coins, int amount) {int[] f = new int[amount + 1];Arrays.fill(f, Integer.MAX_VALUE);f[0] = 0;for (int i = 1; i <= amount; i++) {//如果通过放这个硬币能够达到数量ifor(int coin : coins) {if (i >= coin && f[i - coin] != Integer.MAX_VALUE)// 获得i的数量的硬币数就可能是获得i-A[j]重量硬币数的方案+1// 拿这个方案数量与原本的方案数打擂台,取最小值就行f[i] = Math.min(f[i - coin] + 1, f[i]);}}if (f[amount] == Integer.MAX_VALUE) {return -1;}return f[amount];}
}

总结

  1. 这是求最值问题,用动态规划方式求解。(案例:最少硬币组合)
  2. 进入求解过程,先确定问题状态
    • 提炼最后一步(最优策略中使用的最后一枚硬币aK
    • 子问题转化(最少的硬币拼出更小的面值X-aK
  3. 构建转移方程(f[X] = min{f[X-2]+1, f[X-5]+1, f[X-7]+1}
  4. 设置初始条件和边界情况(f[0] = 0, 如果不能拼出Y,f[Y]=正无穷
  5. 确定计算顺序并计算求解(f[0], f[1], f[2],…)

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

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

相关文章

php ajax队列,AJAX请求队列实现

这篇文章主要为大家详细介绍了AJAX请求队列的实现代码,具有一定的参考价值&#xff0c;感兴趣的小伙伴们可以参考一下AJAX在使用的过程中会遇到一个问题&#xff0c;当用户短时间内执行了多个异步请求的时候&#xff0c;如果前一个请求没完成&#xff0c;将会被取消执行最新的一…

php Spreadsheet 导出,PhpSpreadsheet 导出Excel

/*** Excel 助手* sudo composer require phpoffice/phpspreadsheet*/namespace CommonUtil;use PhpOfficePhpSpreadsheetSpreadsheet;use PhpOfficePhpSpreadsheetWriterXlsx;use PhpOfficePhpSpreadsheetStyleAlignment;use PhpOfficePhpSpreadsheetStyleColor;class ExcelUt…

php 不同时区时间转换,在PHP中将DateTime字符串转换为不同的时区

好吧,我有以下代码$from "Asia/Manila";$to "UTC";$org_time new DateTime("2012-05-15 10:50:00");$org_time $org_time->format("Y-m-d H:i:s");$conv_time NULL;$userTimezone new DateTimeZone($from);$gmtTimezone new…

php iis ajax 无效,IIS7中Ajax.AjaxMethod无效的原因及解决方法

最近做用Ajax.AjaxMethod方法的时候&#xff0c;在asp.net的服务器下一切正常&#xff0c;用iis的时候&#xff0c;js中总是cs类找不到&#xff0c;我就郁闷了&#xff0c;折腾了大半天&#xff0c;终于找到错误原因了。因为我发布网站用的是iis7&#xff0c;所以在web.config位…

查看oracle监听服务器,处理Oracle 监听文件listener.log问题

如果连接时候变得较慢 查看Oracle日志记录&#xff0c;可能是因为此文件太大&#xff0c;超过2G&#xff0c;需要定期清理&#xff0c;(如果多用户&#xff0c;记得用root&#xff0c;可能没权限)查看listener.log&#xff1f;find / -name listener.log经查看&#xff0c;竟然…

oracle添加伪列,Oracle伪列 - jifengtang的个人空间 - OSCHINA - 中文开源技术交流社区...

在oracle10g和下&#xff0c;伪列包括如下内容&#xff1a;lHierarchical Query Pseudocolumns分级查询是oracle提供的递归查询语法&#xff0c;在这里不做展开。只有在分级查询下&#xff0c;才可以使用以下伪列&#xff1a;1.CONNECT_BY_ISCYCLE Pseudocolumn2.CONNECT_BY_IS…

美国oracle球场,美国体育馆考察——美国体育产业是如何盈利的?

体育是美国一项较高利润的产业&#xff0c;其发展规模、发展水平和效益都是世界一流的。美国体育馆考察&#xff0c;主要考察美国体育产业的盈利模式和体育赛事的赞助模式以及球馆的运营管理&#xff0c;并对比中美体育产业的差异&#xff0c;从中获得先进的体育产业运营思维&a…

php集成环境怎么打开,PHP集成开发环境PhpStorm快速入门指南(二):打开一个项目...

PhpStorm是一个轻量级且便捷的PHP IDE&#xff0c;其旨在提高用户效率&#xff0c;可深刻理解用户的编码&#xff0c;提供智能代码补全&#xff0c;快速导航以及即时错误检查。可随时帮助用户对其编码进行调整&#xff0c;运行单元测试或者提供可视化debug功能。PhpStorm 2019.…

如何查询oracle最近报警信息,教你怎样用Oracle方便地查看报警日志错误

在网上查了几天的资料&#xff0c;尝试综合清除告警日志内容及建外部表的方式来解决这一问题。一&#xff1a;备份并清除告警日志内容将每天的告警日志备份好&#xff0c;然后进行清除。1:备份报警日志在$ORACLE_HOME/SID/bdump/ 目录下&#xff0c;按日期备份alert_ORACLE_你…

计算机分php,计算机按照处理数据的形态分类,可以分为什么?

计算机按照处理数据的形态分类&#xff0c;可以分为&#xff1a;1、数字计算机&#xff0c;是以数字形式的量值在机器内部进行运算和存储的电子计算机&#xff1b;2、模拟计算机&#xff0c;是根据相似原理&#xff0c;用一种连续变化的模拟量作为被运算的对象的计算机&#xf…

2.oracle物理结构,oracle实验2oracle物理结构管理

oracle实验2oracle物理结构管理 (6页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;9.9 积分实验2 oracle物理存储结构管理、实验目的1. 掌握物理结构的创建和修改方法2. 掌握表空间的存储参数设置方…

linux mount 查看挂载目录,Linux下使用mount来挂载设备到目录

一般情况下直接mount 设备路径 目录路径&#xff0c;就可以了。umount 设备名&#xff0c;就可以卸载这个设备了使用lsblk -f可以查看挂载的设备&#xff0c;以及这些设备的文件系统。roottao-PC:/boot# lsblk -fNAME FSTYPE LABEL UUID MOUNTPOINTsda├─sda1├─sda2 vfat SY…

centos7是哪种版本Linux,centos7怎么查看系统版本是不是7.2 7.5 7.6

CentOS的版本号信息一般存放在配置文件当中&#xff0c;在CentOS中&#xff0c;与其版本相关的配置文件中都有centos关键字&#xff0c;该文件一般存放在/etc/目录下&#xff0c;所以说我们可以直接在该文件夹下搜索相关的文件。其中存放其版本配置信息的文件为“centos-releas…

linux6.0 安装教程,CentOS 6.0安装步骤

1&#xff0e;安装引导选择安装或升级现有系统(Install or upgrade an existing system)&#xff1a;这个选项是默认的。 选择此选项&#xff0c;安装到您的计算机使用CentOS的图形安装程序的系统。2.检测光盘介质可以选择skip跳过3.选择安装过程中的语言这里选择chinese中文简…

LeetCode - Easy - 14. Longest Common Prefix

Topic String Description https://leetcode.com/problems/longest-common-prefix/ Write a function to find the longest common prefix string amongst an array of strings. If there is no common prefix, return an empty string “”. Example 1: Input: strs […

linux 虚函数调用性能,C++对象布局及多态实现探索之虚函数调用

我们再看看虚成员函数的调用。类C041中含有虚成员函数&#xff0c;它的定义如下&#xff1a;struct C041{C041() : c_(0x01) {}virtual void foo() { c_ 0x02; }char c_;};执行如下代码&#xff1a;C041 obj;PRINT_DETAIL(C041, obj)PRINT_VTABLE_ITEM(obj, 0, 0)obj.foo();C0…

netflow流量分析工具 linux,Centos5/Linux安装Nfdump和Nfsen图形界面分析netflow数据

Nfdump是linux下netflow数据采集分析工具&#xff0c;Nfsen是基于nfdump是web界面工具&#xff0c;服务器需先安装web服务器和php环境。安装rrdtool及所需组件&#xff1a;yum install perl-rrdtool rrdtool rrdtool-devel rrdutils flex byacc安装所需perl模块&#xff1a;yum…

linux嵌入式平台测试,protobuf-c 在arm linux 嵌入式平台的使用 测试

关于什么是protobuf&#xff0c;网上搜搜一大堆&#xff0c;很多人用的都还是json&#xff0c;以为json是多种语言传输数据是万能的&#xff0c;看完了protobuf的实现&#xff0c;就明白了简单高效才是王道。1、首先写一个.proto扩展名的文件json.proto&#xff0c;内容格式如下…

Linux gitpush错误,linux – GIT:无法推送(奇怪的配置问题)

我正在全新安装Linux Mint.尝试从任何存储库推送时,我收到以下错误&#xff1a;error: Malformed value for push.default: simpleerror: Must be one of nothing,matching,tracking or current.fatal: bad config file line 8 in /home/leng/.gitconfigfatal: Could not read …

linux+shell+func,Linux shell编程笔记总结

Linux Shell学习笔记简介Linux系统的shell作为操作系统的外壳&#xff0c;为用户提供使用操作系统的接口。它是命令语言、命令解释程序及程序设计语言的统称。shell是用户和Linux内核之间的接口程序&#xff0c;如果把Linux内核想象成一个球体的中心&#xff0c;shell就是围绕内…