动态规划-不相交的线

动态规划-不相交的线

  1. 前言

动态规划中存在一类问题,它涉及到两个数组或链表,需要求解出两个数组中的最长公共子序列,如果要求解两个数组的最长公共子序列。如果采取最原始的方式,选择对第一个数组中的元素的不同排列进行有序组合枚举(subset),类似采用Powerset的求解方法,紧接着采用KMP方法,在第二个数组中搜索所有的可能的subset序列,优点是理解直观,也容易编程实现;缺点也显而易见,时间和空间复杂度都很高,尤其是时间复杂度,保持在指数级别。

  1. 不相交的线 问题描述

问题来源于Leetcode - 1035,

在两条独立的水平线上按给定的顺序写下 nums1nums2 中的整数。

现在,可以绘制一些连接两个数字 nums1[i]nums2[j] 的直线,这些直线需要同时满足满足:

  • nums1[i] == nums2[j]
  • 且绘制的直线不与任何其他连线(非水平线)相交。

请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。

以这种方法绘制线条,并返回可以绘制的最大连线数。

在这里插入图片描述

这个问题其实可以归结为最长公共子序列问题,由于为最长公共子序列,采用最长公共子序列对应元素绘制的直线不会与其它连线相交,因为公共子序列的搜索是线性模式,一旦搜索完成,不会进行回退处理。

  1. 问题解答

基于《算法导论》中CRCC的四步方法模板,我们对此问题进行深入剖析和理解。第一步为表征最优子问题的解结构(Characterize the structure of the optimal solution),最优子问题一般表现为求最大、最小或者满足特定条件的解数量。

a.) 表征最优子问题的解结构

首先我们需要找到子问题的表征方式,如果采用F(i,j)来表征可以绘制的最大连线数,那么子问题有哪些呢? 我们分为2种不同情况来分别探讨,

  • 如果nums1[i]的元素和nums2[j]的元素,二者互相相等,这种情况下,F(i,j)问题的值就需要依赖F(i-1,j-1),然后再加上1即可。也可以理解此时,问题规模都缩小1,同时,带来的连线数目也相应增加1.
  • 如果nums2[i]位置上元素和nums2[j]元素不相等,这种情况下,F[i,j]的值就依赖于F(i-1,j)或F(i,j-1),如果把F(i-1,j)或F(i,j-1)的描述可视化,意思就是两个数组当前位置元素既然不等,那么F(i-1,j)就表示nums1中的元素回退一个位置,然后再和数组nums2的元素进行比较;F(i,j-1)表示数组nums1元素位置保持不变,让nums2当前元素位置回退1,然后在进行相关的比较。

此时子问题的结构就显现出来,原来的问题可能是三个子问题中的任何一个,所以在程序中需要采用判断进行计算,由于判断的条件为相等或者不相等,那么整个问题的递归在空间上就构造出一颗二叉树(严格意义上来说,是带条件的的动态二叉树)。

b.) 采用递归的方式定义最优解(Recursively define the value of the optimal problem)

递归问题需要采用“从大处着眼,从小处着手”的原则,头脑中谨记递归的全局观念,那么此问题的大处可以表征为F(i,j) 其中i表示nums1的元素下标,j表示nums2的下标,按照上面的条件:
F ( i , j ) = { F ( i − 1 , j − 1 ) } + 1 ;   i f   n u m s 1 [ i ] = = n u m s 2 [ j ] F ( i , j ) = m a x { F ( i − 1 , j ) , F ( i , j − 1 ) }   n u m s 1 [ i ] ≠ n u m s 2 [ j ] F(i,j)=\{F(i-1,j-1)\}+1;\ if \ nums1[i]==nums2[j] \\F(i,j)=max\{F(i-1,j),F(i,j-1)\} \ nums1[i]≠nums2[j] F(i,j)={F(i1,j1)}+1; if nums1[i]==nums2[j]F(i,j)=max{F(i1,j),F(i,j1)} nums1[i]=nums2[j]
采用递归定义的问题中,我们发现存在求最大值的问题,同时也有选择带来绘制线条数目增加的问题,其代价是1而已。接下来,自然而然会有问题,递归当中是否存在重合的子问题呢? 答案是肯定的,只是比较隐蔽而已。采用画图的方式对此进行进一步的解释和深入的理解。

在这里插入图片描述

红色椭圆内的两个子问题,可以归结问同一个问题,也就是此问题展现出重叠子问题的基本特征。有了重叠子问题和最优解结构两大特征,我们可以声明此问题可以采用动态规划加以解决。

c) 计算最优解的值(Compute the value of the optimal solution),一般情况下可以选择递归或迭代方式。

如果要采用递归的方式,那么就需要确定递归出口条件,经过上图观察发现,当任意一个数组中的元素个数为0的时候,此时返回0的值。这个其实也非常容易理解,如果某个数组不含有元素,那么就无法进行元素之间的连线,所以结果自然而然等于0.

递归过程中,实际上由于条件的判定,递归树出现了剪枝,剪枝的导致的结果使三叉树直接退化为二叉树或线性表(单树)。我们可以看到F(nums1[1],nums2[1,3,9])实际上是单个树,其后由于条件不同,又演化为二叉树。

d) 代码实现

代码实现过程非常简单,采用naive的递归方式,理解简单原始的问题解决方式。

函数实现代码:

/**
 * @file max_uncrossed_lines.c
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-03-27
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef MAX_UNCROSSED_LINES_C
#define MAX_UNCROSSED_LINES_C
#include "max_uncrossed_lines.h"

int max_uncrossed_lines(int *nums1, int *nums2, int i, int j)
{
    if(i==0 || j==0)
    {
        return 0;
    }

    int max_lines;
    int line_1;
    int line_2;

    if(nums1[i-1]==nums2[j-1])
    {
        max_lines=max_uncrossed_lines(nums1,nums2,i-1,j-1)+1;
    }
    else
    {
        line_1 = max_uncrossed_lines(nums1, nums2, i - 1, j);
        line_2 = max_uncrossed_lines(nums1, nums2, i, j - 1);

        max_lines=(line_1>=line_2?line_1:line_2);
    }

    return max_lines;
}

#endif

测试代码

/**
 * @file max_uncrossed_lines_main.c
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-03-27
 * 
 * @copyright Copyright (c) 2023
 * 
 */

#ifndef MAX_UNCROSSED_LINES_MAIN_C
#define MAX_UNCROSSED_LINES_MAIN_C
#include "max_uncrossed_lines.c"

int main(void)
{
    int nums1[] = {1,2,3,7};
    int nums2[] = {1,3,9,2};
    int nums1_size=sizeof(nums1)/sizeof(int);
    int nums2_size = sizeof(nums2) / sizeof(int);

    int max_value;

    max_value=max_uncrossed_lines(nums1,nums2,nums1_size,nums2_size);

    printf("The max number of uncrossed line is %d\n",max_value);

    getchar();

    return EXIT_SUCCESS;


}

#endif

  1. 小结

面对不相交的线问题,我们转换为LCS问题,利用剪枝条件,把三叉树退化为二叉树或线性列表,这个过程中不断利用剪枝条件对问题进行深度优先遍历,最后结合各个子问题的解,得到最原始问题的答案。

参考资料:

1035. 不相交的线 - 力扣(Leetcode)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Excel:vlookup函数

Excel:VlookUp函数VlookUp函数VlookUp函数 首先还是先放官方文档的参考:VLOOKUP 函数 Vlookup函数参数: VLOOKUP(lookup_ value, table_ array, col index_ num, [range_ lookup]) lookup_ value:要查找的内容; table_ array&a…

CloudCompare 二次开发(6)——插件中拖拽添加Qt窗口(区域生长算法为例)

目录 一、概述二、插件制作三、Cmake编译四、插件代码五、结果展示一、概述 手动拖拽的方式搭建Qt对话框界面的制作流程,以PCL中的点云区域生长算法为例进行制作。 二、插件制作 1、将....\plugins\example路径下的ExamplePlugin复制一份并修改名字为CCPointCloudProcess。 …

大数据之Spark基础环境

文章目录前言一、Spark概述(一)Spark是什么(二)Spark的四大特点(三)Spark的风雨十年(四)Spark框架模块(五)Spark通信框架总结前言 #博学谷IT学习技术支持# 本…

【lwIP(第四章)】网络接口

目录一、lwIP网络接口简介二、lwIP的netif结构三、lwIP的netif相关函数1. lwIP网络接口的全局变量2. netif_add()函数3. netif_remove()函数4. netif_set_default()函数一、lwIP网络接口简介 lwIP协议栈支持多种不同的网络接口(网卡),由于网卡…

OSPF----优化

优化主要目的---减少LSA的更新量以及数量 路由汇总(减少骨干区域的LSA更新量)OSPF特殊秋雨(减少非骨干区域的LSA更新量)OSPF路由汇总(路由聚合) OSPF路由汇总是由手工部署的OSPF的汇总称为---区域汇总&…

Swagger快速入门【基础总结】

Swagger 背景信息 什么是前后端分离: 即: Vue Springboot 开发模式 以前是后端时代(后端是主力):前端只用管理静态页面;html—>后端。 前后端分离时代: 前端 :前端控制层、视图层【前端团队】后端:后…

客户端安装SSH工具Xshell图解

一、客户端安装SSH工具 windows客户端:安装Putty、XShell 或者 SecureCRT Linux客户端:yum install openssh-clients macOS客户端:默认已经安装了SSH客户端 我们这里安装windows客户端,选择XShell 工具。 Xshell5、Xftp5下载&am…

Linux系统之安装PostgreSQL数据库

Linux系统之安装PostgreSQL数据库一、PostgreSQL介绍1.PostgreSQL简介2.PostgreSQL特点二、本次实践介绍1.本次实践介绍2.实践环境介绍三、配置PostgreSQL的yum仓库源1.检查本地是否部署PostgreSQL2.配置镜像源3.检查yum仓库镜像源状态四、安装PostgreSQL1.安装PostgreSQL2.初始…

GPIO的八种模式分析

GPIO是general purpose input output,即通用输入输出端口,作用是负责外部器件的信息和控制外部器件工作。 GPIO有如下几个特点:1.不同型号的IO口数量不同;2,反转快速,每次翻转最快只需要两个时钟周期,以ST…

dubbo的SPI机制和服务暴露,引用原理

一、SPI引入:spi标准:1、需要在 classpath 下创建一个目录,该目录命名必须是:META-INF/service2、在该目录下创建一个 properties 文件,该文件需要满足以下几个条件 :2.1 文件名必须是扩展的接口的全路径名…

量子运算-比算子描述更广泛的一类刻画量子态在客观世界演化的数学工具

参考链接:1.1 量子运算 - 知乎 (zhihu.com)一个量子操作(包括量子测量和量子信道)指的是把一个密度矩阵变成另一个密度矩阵的变换,一般记为 背景演化算符是酉的。这里考虑考虑特殊的演化-测量。测量对应的算子是投影算子&#xff…

刘禹锡最经典诗文10首,每一首都是千古名作,读懂受益一生

他是唐代最乐观的诗人,是比他的好友乐天更乐天的人!他与柳宗元并称“刘柳”,与韦应物、白居易合称“三杰”,并与白居易合称“刘白”。他是在唐代诗人中,出了名的豪放豁达的刘禹锡。白居易称他为“诗豪”。自“永贞革新…

Elasticsearch:理解 Master,Elections,Quorum 及 脑裂

集群中的每个节点都可以分配多个角色:master、data、ingest、ml(机器学习)等。 我们在当前讨论中感兴趣的角色之一是 master 角色。 在 Elasticsearch 的配置中,我们可以配置一个节点为 master 节点。master 角色的分配表明该节点…

【javaEE】阻塞队列、定时器、线程池

目录 🌴一、阻塞队列 1.概念 2.生产者消费者模型 3.阻塞队列的实现 🏹二、定时器 1.引出定时器 2.定时器的实现 🔥三、线程池 1.引出线程池 2.ThreadPoolExecutor 构造方法 3.标准数据库的4种拒绝策略【经典面试题】【重点掌握】 …

2020年第十一届C/C++ B组第一场蓝桥杯省赛真题

准备参加第十四届蓝桥杯,今天开始刷题目的第一天,下面是2020年第十一届C/C B组第一场蓝桥杯省赛真题,以下是我的做题目心得。跑步训练第一次写的代码失误点如下:第一个错误点是因为好久没有写代码,忘记判断对才能循环&…

【SCL】博图——先入先出排序法

使用博图SCL语言来实现先入先出排序 前言 使用SCL完成一个先入先出排序 具体要求:最先输入的一个数值,最先输出出来,下面的数自动向前填充; 注:这里可能有两种理解:一是第一个输入的第一个出来&#xff…

解析vue中的process.env

一、介绍 1、process process是 nodejs 下的一个全局变量,它存储着 nodejs 中进程有关的信息。 2、process.env env 是 environment 的简称,process.env属性返回一个包含用户环境的对象。 3、dotenv Dotenv 是一个零依赖的模块,它能将环境变…

蓝桥杯刷题冲刺 | 倒计时16天

作者:指针不指南吗 专栏:蓝桥杯倒计时冲刺 🐾马上就要蓝桥杯了,最后的这几天尤为重要,不可懈怠哦🐾 文章目录1.青蛙跳杯子1.青蛙跳杯子 题目 链接: 青蛙跳杯子 - 蓝桥云课 (lanqiao.cn) X 星球的…

用于人工智能研究的开源Python微电网模拟器pymgrid(入门篇)

pymgrid是一个开源Python库,用于模拟微型电网的三级控制,允许用户创建或自行选择的微电网。并可以使用自定义的算法或pymgrid中包含的控制算法之一来控制这些微电网(基于规则的控制和模型预测控制)。 pymgrid还提供了与OpenAI Gy…

初识冯诺依曼体系结构

目录 1.冯诺依曼体系结构 2.冯诺依曼体系的原理 3.数据流向 4.冯诺依曼体系的意义 1.冯诺依曼体系结构 我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。 (1)输入单元:…