动态规划:单调减子序列

一个算法,题目如下:

        从一个由N个整数排列组成的整数序列中,自左向右不连续的选出一组整数,可以组成一个单调减小的子序列(如从{68 69 54 64 68 64 70 67 78 62 98 87}中我们可以选取出{69 68 64 62}这个子序列;当然,这里还有很多其他符合条件的子序列)。给定整数序列的长度和整数序列中依次的值,请你求出这个整数序列中“最长的单调减小的子序列的长度”以及“不同但长度都是最长得单调减小的子序列的数量”。

       输入第1行为一个整数N,表示输入的整数序列的长度(1≤N≤50000)。输入第2行包括由空格分隔的N个整数(每个整数都在32位长整型范围内)。
输出包括一行,为两个数字,分别为针对给定的整数序列求出的“最长的单调减小的子序列的长度”以及“值不同但长度都是最长得单调减小的子序列的数量”
样例输入
12
68 69 54 64 68 64 70 67 78 62 98 87
样例输出
4 2

分析:

        求序列中的最长单调递增或递减序列长度的动态规划算法网上到处都是,基本思想就是把之前不同长度的最长子序列的最大/小最后一个数给记下来,然后依次的更新后面的数,更新时采用二分查找可以提高效率,缩小复杂度。可以自己先想一下怎么实现,不要直接去看代码,会被搞得云里雾绕的,一些文章写得不是很清晰,代码质量也一般,所以我一般都是先想一下自己会怎么实现,然后再看别人的讲解,代码其次,因为有时候看别人代码,“就跟被强x了似的”(别人说的)。

       但本题目同时要求出最长子序列的数量,这就不一样了,比如这个序列,4 5 2 1,最长递减子序列长度是3,但4 2 1和5 2 1都是最长递减子序列,个数为2.再比如2 1 2 1最长递减子序列长度是2,虽有两个2 1子序列,但只能算1个,因为子序列相同。

       因为一开始没有理解题意,认为2 1 2 1这种情况只要选取子序列时的位置不同就算一个子序列,那么此时就会算有3个最长子序列,虽然内容相同,都是2 1,但选取的元素的位置不同,耗费了大量时间,把代码搞得很复杂,当然出题人也可以这么设计题目。本题中题目意思经测试不是这个意思,2 1 2 1只能算有1个长度为2的内容为2 1的最长子序列。

       现在我们知道了题意,就可以找几个例子来设计一下算法。因为求递减子序列,我的想法是从最后一位进行处理,每次处理时找出以本数为起始时的最长子序列长度及个数,然后不断的往序列前面处理,直到序列处理完毕,然后统计出最长的子序列个数。关键算法在于找出以本数为起始时的最长子序列长度及内容不同最长子序列个数,根据动态规划的思想,将以每个数为起始的长度、下标记录,然后处理序列前面的数时,拿这个数与之前记录中的每个下标位置的数进行比较,求出若能构成最长的子序列,若构成最长子序列的个数不是1个,那么有几个就记录几个长度和下标,此处我用了multimap<int,int>来进行存储长度和下标键值对,因为multimap允许重复键存在。

       找个特殊一点的例子进行举例分析,比如4 3 2 5 3 2这个序列,从后往前处理,最后一位为2,最长子序列长度位1,存储长度1、数组下标5;那么插入<1,5>;然后是3,根据存储内容,插入一个<2,4>,此时multimap为:<1,5> <2,4>;然后是5,插入<3,3>,此时multimap为:<1,5> <2,4> <3,3>;接下来处理2,因为之前有了2这个长度为1的子序列,且与之前的2之间不存在比2小的数,所以没有产生更长的子序列,所以这里不必记录,multimap不变;然后是3,同样可以查出之前有3存在࿰

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值