一维动态规划-拾取硬币

2023-10-30

1.问题引入

假如有n个硬币排在一行,如: c [ 0 ] , c [ 1 ] , … , c [ n − 1 ] c[0],c[1],\dots ,c[n-1] c[0],c[1],,c[n1]

要求不能拾取相邻的两个硬币,以获得累加面值最大的拾取子序列。比如,有面值如下的硬币: 5 , 1 , 2 , 10 , 6 , 2 5,1,2,10,6,2 5,1,2,10,6,2

可以拾取5+2+6=13,也可以拾取1+10+2=13。以上两种拾取的硬币均不相邻,因此都是符合要求的拾取方式。但是,最大总面值的拾取应是5+10+2=17。这个拾取首先没有相邻的硬币,而且是所有可行拾取中累加面值最大的一个拾取。

以上问题最直接的算法是求出所有可行的拾取子序列,并求出它们各自的累加值,其中累加值最大的序列就是所求结果。也就是,从输入序列中穷举所有可行序列。由于序列中每一个元素要么在所求序列中,要么不在,这样将有 2 n 2^n 2n个可行序列。因此,采用穷举法来求解该问题的算法效率是指数规模,下面将介绍通过动态规划来获得一个更为高效的算法。

2.定义子问题

不妨设从硬币 c [ 0 ] c[0] c[0]直到 c [ i ] c[i] c[i]中累加和最大的值为 c o l l e c t _ c o i n s ( i ) collect\_coins(i) collect_coins(i).如果将每一个硬币都当成一个字符,输入硬币的前缀 c [ : i ] c[:i] c[:i]就是定义的子问题,该子问题的求解函数示为 c o l l e c t _ c o i n s ( ) collect\_coins() collect_coins()。由于 i i i取值 [ 0 , n − 1 ] [0,n-1] [0,n1],因此子问题的个数为 n n n

3.猜测解

利用动态规划求解斐波那契数时,由于直接给出了斐波那契数的递归式,因此并不需要猜测解这一步。然而,当利用动态规划求解大部分问题时,都需要我们构建问题的递归解。为了求得子问题的解,就需要使用猜测这一方法。猜测就是一种尝试或者假设。 对于子问题 c [ : i ] c[:i] c[:i],考察硬币 c [ i ] c[i] c[i],它存在两种可能的猜测:

  • 最优解不包括第 i i i个硬币。那么前i个硬币累加和最大值应等于 c o l l e c t _ c o i n s ( i − 1 ) collect\_coins(i-1) collect_coins(i1)

  • 最优解包括第 i i i个硬币。那么前i个硬币累加和最大值则等于 c o l l e c t _ c o i n s ( i − 2 ) + c [ i ] collect\_coins(i-2)+c[i] collect_coins(i2)+c[i]

在子问题 c [ : i ] c[:i] c[:i]的解中,我们考察硬币 c [ i ] c[i] c[i]。对于 c [ i ] c[i] c[i],不妨先猜测最优解中不包括硬币 c [ i ] c[i] c[i],则子问题 c [ : i − 1 ] c[:i-1] c[:i1]的解等于子问题 c [ : i ] c[:i] c[:i]的解。比如c=[5,1, 2,10,6],该子问题的解为5+10=15,最后的硬币6不在最优解中,那么将硬币6拿开后剩余的子问题为 c [ : i − 1 ] c[:i-1] c[:i1]=[5,1,2,10],这个子问题的解依然是5+10=15。

此外,硬币 c [ i ] c[i] c[i]也可能就在最优解中,那么子问题 c [ : i ] c[:i] c[:i]的解应等于子问题 c [ : i − 2 ] c[:i-2] c[:i2]的解加上硬币 c [ i ] c[i] c[i]的面值。比如,子问题 c [ : i ] c[:i] c[:i]=[5,1,2,10],其最优解为5+10=15。硬币10在最优解中,那么子问题 c [ : i − 2 ] c[:i-2] c[:i2]=[5,1]的最优解为5,因此子问题 c [ : i ] c[:i] c[:i]| 的最优解15等于子问题c|:i-2]的最优解5加上 c [ i ] c[i] c[i]=10。

4.子问题之间的递归关系

c o l l e c t    ‾ c o i n s ( i ) = m a x { c [ i − 1 ] + c o l l e c t    ‾ c o i n s ( i − 2 ) , c o l l e c t    ‾ c o i n s ( i − 1 ) , i ⩾ 2 } ( 1 ) \begin{aligned} collect \underline{~~} coins(i)=max\{c[i-1]+collect \underline{~~} coins(i-2),collect \underline{~~} coins(i-1),i \geqslant 2\} (1) \end{aligned} collect  coins(i)=max{c[i1]+collect  coins(i2),collect  coins(i1),i2}(1)

该递归式表明 c o l l e c t _ c o i n s ( i ) collect\_coins(i) collect_coins(i)要么等于 c o l l e c t _ c o i n s ( i − 1 ) collect\_coins(i-1) collect_coins(i1),要么等于 c o l l e c t _ c o i n s ( i − 2 ) + c [ i − 1 ] collect\_coins(i-2)+c[i-1] collect_coins(i2)+c[i1]。由于原问题是求最大的累加面值,因此 c o l l e c t _ c o i n s ( i ) collect\_coins(i) collect_coins(i)的值应该等于它们之中较大的那个。当没有硬币时, c o l l e c t c o i n s ( ) = 0 collect_coins()=0 collectcoins()=0。只有一个硬币的话, c o l l e c t c o i n s ( ) collect_coins() collectcoins()应该等于当前的硬币面值。因此,式(1)的边界条件为, c o l l e c t    ‾ c o i n s ( 0 ) = 0 , c o l l e c t    ‾ c o i n s ( 1 ) = c [ 0 ] \begin{aligned} collect \underline{~~} coins(0)=0,collect \underline{~~} coins(1)=c[0]\end{aligned} collect  coins(0)=0,collect  coins(1)=c[0] 递归式(1)存在诸多重复的子问题,如 c o l l e c t _ c o i n s ( 1 ) , c o l l e c t _ c o i n s ( 2 ) , c o l l e c t _ c o i n s ( 3 ) collect\_coins(1),collect\_coins(2), collect\_coins(3) collect_coins(1),collect_coins(2),collect_coins(3)等。此外,还存在优化的子结构,也就是原问题的最优解可由各个子问题的最优解合成得到。这意味着如果采用自底向上的方法实现递归式(1),可以获得较高的执行效率。

5.自底向上构造动态规划表

建立了子问题间的递归关系,就可以利用自底向上的方法求解递归关系。自底向上实现递归的本质就是填表,我们称该表为动态规划表。一个子问题就对应于表格的一个单元格,每一个单元格的值经由递归式来完成计算。因此,单元格在计算过程中存在相互依赖关系。

为了保证动态规划表内每一个单元格在计算过程中都有足够的数据,因此填表的过程必须满足拓扑排序。也就是说,表中某个单元的值只依赖于已有值的单元格。

6.Python实现时间复杂度O(n)算法求解拾取硬币

def bottom_top_collect_coins(row_coins):
    """
    自底向上实现递归策略获得累加面值最大拾取硬币子序列的所有子问题的动态规划表
    :param row_coins: 原始硬币列表
    :return: 拾取累加面值最大拾取硬币的子序列索引,最大面值
    """
    i = coins_num = len(row_coins)
    dp_table = [0] * (coins_num + 1)  # 初始化动态规划表
    dp_table[1] = row_coins[0]
    collect_trace = []  # 初始化拾取获得累加面值最大拾取硬币的子序列
    for k in range(2, coins_num + 1):  # 动态规划表比硬币序列长一位
        dp_table[k] = max(dp_table[k - 2] + row_coins[k - 1], dp_table[k - 1])
    # 回溯获得拾取序列索引
    while i >= 1:
        if dp_table[i] > dp_table[i - 1]:  # 确定拾取第i-1个硬币
            collect_trace.append(i - 1)
            i -= 2
        else:
            i -= 1
    collect_trace = collect_trace[::-1]  # 调整倒序累加面值最大拾取硬币的子序列为正序
    return collect_trace, dp_table[-1]

7.测试代码

if __name__ == '__main__':
    coins = [5, 1, 2, 10, 6, 2]
    track, max_sub_seq_value = bottom_top_collect_coins(coins)
    print("""原始硬币序列为:{}
拾取累加面值最大硬币的子序列索引为:{}
累加最大面值为:{}""".format(coins,track,max_sub_seq_value ))

8.运行截图

拾取硬币运行结果

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一维动态规划-拾取硬币 的相关文章

随机推荐

  • [论文阅读] Tokens-to-Token ViT: Training Vision Transformers from Scratch on ImageNet

    论文地址 https arxiv org abs 2101 11986 代码 https github com yitu opensource T2T ViT 发表于 ICCV 2021 Arxiv 2021 01 Abstract Tra
  • Spring Boot 项目结构

    简介 Spring Boot 根据实际的项目可以有不同的文件结构 比如使用maven还是使用gradle构建工具 开发Web项目还是控制台项目 使用JPA文件结构和使用Mybatis的文件结构 前后端分离项目它们采用的目录结构是不同的 但它
  • 汽车部件IPX9K/IP69K、IP66K等ip防护等级测试的应用

    汽车部件IPX9K IP69K IP66K等ip防护等级测试的应用 汽车传感器 连接器 水泵 灯具等部件的ip防护等级测试 以IPX9K IP69K IPX5 IPX6 IPX6K IPX7 IPX8 IP5X IP6X测试为主流 其中高等
  • Flutter——最详细(GridView)使用教程

    GridView简介 可以创建网格列表视图 主要通过Count extent custom builder构造列表 有内边距 是否反向 滑动控制器等属性 四个属性使用场景 Count extent custom适用于子布局较少时使用 可能会
  • 【区块链共识算法】-PoW算法

    极客时间 工作量证明 比如小李来 BAT 面试 说自己的编程能力很强 那么他需要做一定难度的工作 比如做个编程题 根据做题结果 面试官可以判断他是否适合这个岗位 工作对于请求方是有难度的 对于验证方则是比较简单的 易于验证的 Pow算法 计
  • vue如何制作动态效果的进度条

    vue如何制作动态效果的进度条 先看效果图 制作这样动效的进度条其实很简单 我们只需要将进度条原本的背景通过transparent画出如下图片的效果 之后我们通过keyframes设置背景的动画效果就做成了 下面上代码 首先进度条的进度提示
  • 使用Python爬取实时天气信息: 如何构建自己的气象观察站

    目录 步骤1 选择天气网站 步骤2 发送HTTP请求 步骤3 解析HTML内容 步骤4 提取天气数据
  • 1. CUDA安装失败解决方法

    CUDA安装失败原因 一般CUDA安装失败都是由于其中Visual Studio VS Intergration无法安装导致的 当然可以通过自定义的方式取消Visual Studio Intergration进行安装 然后再重新用CUDA安
  • zookeeper最新版3.6.2单机、集群

    Linux安装zookeeper3 6 2单机 集群 注意 需要先安装JDK 可以参考这里 Linux 安装JDK1 8 1 下载 wget http mirror bit edu cn apache zookeeper zookeeper
  • 学习Node.js的基础知识和核心概念(全面)

    Node js 这个神奇的技术 融合了前端与后端的力量 让JavaScript在服务器端发挥了异乎寻常的魔力 本文将通过代码和文字解释 全面介绍Node js的特点 从异步非阻塞I O到强大的模块系统 再到丰富的包管理和事件驱动编程 一步步
  • JAVA同步代码块 & 同步方法

    JAVA同步代码块 同步方法 为了解决多线程操作共享数据时产生的安全问题 例如以下代码 if ticket lt 0 卖完了 break else ticket System out println Thread currentThread
  • dss_linkis(datasphere studio-1.1.1、linkis-1.1.1)基础框架安装

    目录 一 基础框架安装 1 1 所需的环境 1 2 环境部署 1 3 dss linkis安装 一 基础框架安装 1 1 所需的环境 我的安装环境如下 与官网给出的相差一点点 CentOS7 DataSphere Studio1 1 1 J
  • 线程的局部变量——ThreadLocal

    ThreadLocal是什么 对这个词语分解 将其分为Thread和Local 顾名思义便是本线程的变量 既然是当前线程的变量 那么就意味着这个变量对于其他线程来说就是隔离的 也就是不可见的 ThreadLocal对每一个线程都有一个副本
  • Eclipse汉化教程

    前言 首次使用Eclipse时 我们对那些不知道的英语都感到迷惑 很多人都会上X度查 那么 如果不用X度 我们该如何进行汉化呢 1 Babel链接获取 到Eclipse Babel Project Downloads获取Babel链接 如图
  • TCP的四个拥塞控制算法

    目录 假定 慢开始 拥塞避免算法 快重传 快恢复 假定 cwnd 拥塞窗口 swnd 发送窗口 swnd cwnd ssthresh 门限值 发送方维护一个叫做拥塞窗口cwnd的状态变量 其值取决于网络的拥塞程度 并且动态变化 拥塞窗口cw
  • 【C语言进阶】⑥函数指针详解

    一 函数指针 1 概念 函数指针 首先它是一个指针 一个指向函数的指针 在内存空间中存放的是函数的地址 请看示例 int main int a 10 int pa a char ch c char pc ch int arr 10 0 in
  • java调用C或者C++动态库dll

    java调用C或者C 动态库dll 本文章使用的是IntelliJ IDEA Community Edition 2021 2 3版本测试的 1 新建项目 linjie demo 添加类HelloLinjie 2 选择项目 新建 目录 输入
  • redis 5 HyperLogLog 布隆过滤器 GeoHash 和 scan

    空闲的时候可以用root登录服务器 玩下左轮手枪 RANDOM 6 0 rm rf echo Clicks 这次我们一起来看下redis的HyperLogLog 布隆过滤器 GeoHash 和 scan HyperLogLog 先看个场景
  • 第1章 前 言

    来源 我是码农 转载请保留出处和链接 本文链接 http www 54manong com id 1258 1 1 问题的背景 1 1 1 RFID技术 RFID即无线射频识别技术 Radio Frequency Identificatio
  • 一维动态规划-拾取硬币

    1 问题引入 假如有n个硬币排在一行 如 c 0 c 1