动态规划,听起来华丽而又高贵,不禁让许多同学望而却步。实际上它是一只披着狼皮的羊,名字唬人,细品起来,这类问题的解题思路还是十分有规律可循的!接下来就让我来由易到难,跟大家聊一聊这只披着狼皮的小绵羊~


引子——硬币问题:

动态规划,显然整个题目的运行求解过程都是在“运动”的,这就代表前一步牵动了下一步的走向,问题在一步一步的移动中得到了最终的解,而如何从最初的问题确定到最终的结果,我们需要有一个东西来记录每一步问题求解到什么状态转移到下一步需要怎样的转移过程等等。这也就是我们常说的状态转移。实际上所有的动态规划问题都是利用这个转移的过程逐步由子问题推广到最终问题的解

会不会有人奇怪,为什么不直接入手最经典的“背包问题”呢,原因很简单,有比背包问题更加简单的问题(没错没错,你没有听错),那就是接下来我要说的硬币问题了。废话不多说,听题~


1.最少硬币问题

有n种硬币,面值分别为v1, v2, ...... , vn,每种硬币数量无限。输入非负整数s,选用硬币,使其和为s。要求输出数量最少的硬币组合数目。

注意题干信息,数量无限是一个很关键的条件。它意味着在选择硬币时,不需要考虑硬币够不够用,能用几枚(这里不理解的话,举个小例子,你只有1元,5元两种硬币,如果数量不限,那么凑出和s为10的最少数目方案显然是2——两枚5元;但如果限制5元硬币只有1枚,那么最少数目方案就变成了6——一枚5元,五枚1元)。在这里一定要留下一个深刻的印象,因为我们后来涉及到的许多问题都是在这上面做了文章的。

那么我们来分析一下这个问题的求解方式。如果我们暴力求解,找出所有的方案数并取最小值,那么枚举量将是不可估量的一个巨大数字:                 

显然,我们不可能用这样一种憨憨方式去求解(不然为什么要学算法)。那么,根据最开始的步骤,对此问题展开具体分析过程:

一. 找到整个问题对应求解过程的子过程(子问题的求解过程)。

所谓子问题,就是父问题的一个子集。此题意在让我们找出和为S的硬币数目最少的硬币方案,那我们不妨假设现在已经知道了所有和数小于等于T(0<T<S)的最少数目方案数为t,那么现在让你去求解和为T+1的最少数目方案数,那么你会怎么求呢?简单点想,T+1的最少方案数就是t+1,因为我只需要在和为T的基础上再使用一枚1元硬币就可以满足和为T+1的条件了,对应的硬币数目只由t增加了一枚即t+1。但存不存在这样一种状况呢:假设我已经知道和为4的最少方案数为4(显然只有四枚1元这种方案),但和为5的最少方案数按照我们之前所想,是不是4+1=5呢,显然不是。因为如果和为5的话,我们只需要一枚5元即可满足条件。

不知道举例子举到这里,大家有没有看出一点端倪,我们的状态转移的过程已经在上面的举例中初步成型。在和为4的已有状态下,我们求解和为5的状态的解的过程是这样的:分别用1元硬币、5元硬币去“碰运气”,对应过程为,我可以用4元+1元凑成5元,也可以用0元+5元凑成5元;对应方案的最少数目分别为(重点来了):和为4对应的最少硬币枚数 + 1,和为0对应的最少硬币枚数 + 1。我用表格为大家演示一遍:

这里列举到S = 5时的状态,试想当我们重复这个过程一直到S达到了我们题目要求的那个和数时,这题不也就解完了吗?

到这里,我对状态转移的讲述已经差不多了。

二. 将状态转移过程具体化为公式

        做动态规划,少不了dp两个字母(Dynamic Programming的缩写)。由于我们要记住每一步递推的中间量,所以要开放一个数组来完成这个功能。针对这个问题,我们需要一个数组记录和为S的硬币最少数目,那么很明显,一个一维的规划数组dp[ ]就跃然纸上(虽然这里没有纸)了。

dp[ m ]则用来记录和为m的硬币最少数目。强调一下重点(溜号的赶紧回来了嗷):

第一遍:一定要记住dp[ m ]代表的含义是什么!!!

第二遍:一定要记住dp[ m ]代表的含义是什么!!!

第三遍:一定要记住dp[ m ]代表的含义是什么!!!

必考题,头两年没考,今年肯定考,15分摆在这,爱背不背——不信我跟你赌一个面包机~

相信我,你只要在整个过程种牢记dp数组是干什么的,你的状态转移方程演示过程就会异常清晰。

所以,你轻松地搞定了下面这个式子:

dp[ m ] = min( dp[ m - value[ i ] ] + 1 ),0 ≤ i ≤ 5,value[ i ]代表第i种硬币的面值。

显然,m一定要大于等于value[ i ]才是有效的,这样的废话应该不需要我多说了~

再过一遍这个式子:

当我们对所有的m,遍历过0~5的i时,说明对每一种面值的硬币,我们都碰过运气了;同理,当我们对所有的i,遍历过0~S的m时,说明我们用这一种面值的硬币,对所有的m都尝试过了。而对m和i的遍历,实际上就是我们在模拟整个状态转移的过程,两重循环,交给计算机去处理妥当就可以了,以下两种是等价形式:

for (i = 0; i < 5; i++) //对5种面值硬币做外层循环for (m = value[i]; m <= S; m++) //m从value[i]开始,保证m ≥ value[i]dp[m] = min(dp[m], dp[m - value[i]] + 1); //状态转移
for (m = 1; m <= S; m++) //对硬币价值和数做外层循环for (i = 0; i < 5; i++) if(m >= value[i]) //保证m大于等value[i]dp[m] = min(dp[m], dp[m - value[i]] + 1); //状态转移

详细代码 :

#include <iostream>
#include <string.h>using namespace std;int dp[251];
int value[5] = { 1, 5, 10, 25, 50 };int main()
{memset(dp, 1, sizeof(dp)); //数组初始化,因为我们每一步要用min函数取最小值,将初值设置为一个较大的数字dp[0] = 0; //初值的设置,手动取模拟一次下面的循环,你就会明白为什么要这么做for (int i = 0; i < 5; i++){for (int j = value[i]; j <= 250; j++){dp[j] = min(dp[j], dp[j - value[i]] + 1);}}cout << dp[250] << endl;return 0;
}

怎么样,有没有觉得实在是太简单了,妈妈再也不用担心我学不会动态规划了!!!

有兴趣的同学可以尝试一下 “hdu 2069”(思路稍微有一些转变)。 如果你看到了我说的重点词汇(硬币数量无限),你就会很清楚地知道该从哪里入手去解决这道题的关键点——您的程序应该能够处理多达100枚硬币

题目链接:HDU 2069—Coin Change

顺便贴一下原题:

问题描述
假设有5种类型的硬币:50美分,25美分,10美分,5美分和1美分。我们想用这些硬币换一定数量的钱。
例如,如果我们有11美分,那么我们可以用一枚10美分硬币和一枚1美分硬币,或两枚5美分硬币和一枚1美分硬币,或一枚5美分硬币和6枚1美分硬币,或11枚1美分硬币进行
更改。因此,有四种方法可以用上述硬币更改 11 美分。请注意,我们计算,有一种方法可以改变零美分。
编写一个程序,以查找以美分计入任何金额的更改的不同方法的总
数量。您的程序应该能够处理多达100枚硬币

输入

输入文件包含任意数量的行每个行由数字 (≤250) 组成,金额为美分。

输出

对于每个输入行,输出一行包含使用上述 5 种硬币进行更改的不同方式的数量。

示例输入

11 26

样本输出

4 13

动态规划——详细入门讲解相关推荐

  1. webpack4.x最详细入门讲解

    前言 本文主要从webpack4.x入手,会对平时常用的Webpack配置一一讲解,各个功能点都有对应的详细例子,所以本文也比较长,但如果你能动手跟着本文中的例子完整写一次,相信你会觉得Webpack ...

  2. 动态规划27k字超详细保姆级入门讲解——附DP经典线性、区间、二维图、四维8个模型题解

    动态规划27k字超详细保姆级入门讲解 写在前面: 这篇文章是目前为止我写过最长也是最久的文章,前面关于DP的讲解我查阅了大量的博客资料,学习其他博主对DP的理解,也翻阅了很多经典的纸质书籍,同时做了近 ...

  3. 一看就明白的爬虫入门讲解:基础理论篇

    一看就明白的爬虫入门讲解:基础理论篇 发表于2015-11-13 18:50| 5909次阅读| 来源CSDN| 37 条评论| 作者孔淼 爬虫经验分享HTMLCSSAjaxApp网络 width=& ...

  4. 一看就明白的爬虫入门讲解-基础理论篇(上篇)

    作者:孔淼 关于爬虫内容的分享,我会分成两篇,六个部分来分享,分别是: 1)  我们的目的是什么 2)  内容从何而来 3)  了解网络请求 4)  一些常见的限制方式 5)  尝试解决问题的思路 6 ...

  5. TypeScript超详细入门教程(上)

    TypeScript超详细入门教程(上) 01 开篇词:Hello~TypeScript 01 开篇词:Hello~TypeScript 更新时间:2019-10-30 13:49:46 既然我已经踏 ...

  6. 自学前端设计——【开源骚客】FPGA超详细入门视频教程

    前言 本文基于[开源骚客]FPGA超详细入门视频教程,简单做个笔记 00. FPGA开发软件的安装 Quartus II 13.1 Modelsim Notepad++ Vim 01. 我的第一个FP ...

  7. 544、RabbitMQ详细入门教程系列 -【手动消费确认】 2022.09.05

    目录 一.前言概述 二.配置实现 2.1 XML配置 2.2 MessageListener实现 三.消息预取 四.并发消费 五.参考链接 一.前言概述 RabbitMQ(四) --消费者Consum ...

  8. 使用PyTorch构建神经网络(详细步骤讲解+注释版) 01-建立分类器类

    文章目录 1 数据准备 2 数据预览 3 简单神经网络创建 3.1 设计网络结构 3.2 损失函数相关设置 3.3 向网络传递信息 3.4 定义训练函数train 4 函数汇总 1 数据准备 神经网络 ...

  9. TensorRT详细入门指北,如果你还不了解TensorRT,过来看看吧

    首发于TensorRT详细入门指北,如果你还不了解TensorRT,过来看看吧!,最新回复以及交流请看这里~ 推荐一个深蓝学院的CUDA课程,TensorRT_Tutorial的作者伟哥讲解的,质量很 ...

  10. 【直播】耿远昊:Pandas入门讲解(安泰第四届数据科学训练营)

    Pandas入门讲解 直播信息 主讲人:耿远昊,Datawhale成员,joyful-pandas作者. 直播时间:2021年04月07日 20:00~21:00 直播内容: 时间序列中的必知必会: ...

最新文章

  1. web.py使用要点
  2. 8月21日至8月27日技术积累
  3. 断言(assert)详解
  4. 浅谈C#中一种类插件系统编写的简单方法(插件间、插件宿主间本身不需要通信)...
  5. DEV控件Grid显示行号
  6. 数据结构与算法--图的表示与常用算法
  7. java中add和addall区别,java中list的add与addall方法区别
  8. libuv 原理_nodejs如何利用libuv实现事件循环和异步
  9. AB PLC学习笔记
  10. Vscode下中文乱码问题
  11. 计算机中级应用,计算机办公软件应用: 中级
  12. 利用selenium自动刷新网页
  13. mui用ajax服务器交互,Mui --- app与服务器之间的交互原理、mui ajax使用
  14. HITB AMS 2021 议题分析与学习,感叹华人真多
  15. 为啥要看javac源代码
  16. sql注入预防 [ 光影人像 东海陈光剑 的博客 ]
  17. 16天记住7000考研词汇
  18. 视频直播APP源码,通过css控制div内容展开更多/收起效果
  19. 静态网站(博客)生成器Static Site Generators(SSGs)大集合
  20. x86、x64、x32和64位、32位的区别与联系

热门文章

  1. 峨眉山徒步休闲三日游攻略内附详细时间
  2. python开发cms企业官网用python php_基于Django的Python CMS
  3. flask-uploads
  4. 【高等数学】九种二次曲面及其方程
  5. 半导体Led Driver IC失效分析
  6. 连接服务器失败是什么原因
  7. java突然无法加载主类_Java 找不到或无法加载主类的修复方法
  8. 苹果手机计算机没有记录吗,苹果手机恢复出厂设置备忘录里的便签内容都没了,怎么找回来啊...
  9. SE-Resnext网络搭建及预训练模型
  10. mysql:Prepared statement needs to be re-prepared解决办法