最长递增子序列 动态规划 (附带三种子序列更改方法)

题目(1)

时间限制: 1000 ms
内存限制: 64 MB
代码长度限制: 16 KB
在这里插入图片描述

简单分析

1.最长递增子序列,最长递减子序列,最长不下降子序列,最长不上升子序列,都是一个模子里刻出来的玩意,动态规划即可。
2.以这个数结尾的最长子序列都是由前面的元素组成的,可以得到动态方程 dp[a] = max{dp[n] + 1}(a > n && D(a) > D(n))(D为所有元素组成的集合)
3.时间复杂度为Θ(n2)。

代码

#include <iostream>
#include <stack>
using namespace std;
int main()
{
    //s用于存放路径
    stack<int> s;
    //data记录序列,sign记录该点为结点的最大子序列长度,before记录该点最大子序列之前结点
    //max_int存放最大子序列长度,max_sign存放最大子序列结点,num序列长度
    int data[1000],sign[1000], before[1000];
    int max_int = 1, max_sign = 0 , num;
    //输入数组初始化变量
    cin >> num;
    for(int tmp = 0; tmp < num; tmp++)
        cin >> data[tmp];
    before[0] = 0;
    sign[0] = 1;
    //遍历数组
    for(int tmp = 1; tmp < num; tmp++)
    {
        sign[tmp] = 1;
        //查询tmp之前元素,找出他所在子序列位置,并记录之前结点
        for(int tmp2 = 0; tmp2 < tmp; tmp2++)
            if(data[tmp] > data[tmp2] && sign[tmp2] + 1 > sign[tmp])
            {
                sign[tmp] = sign[tmp2] + 1;
                before[tmp] = tmp2;
            }
        //如果他比最大的大,就记录下来
        if(sign[tmp] > max_int)
        {
            max_int = sign[tmp];
            max_sign = tmp;
        }
    }
    cout <<  max_int <<endl;
    num = 0;
    //通过before数组回溯整个遍历过程
    while(sign[max_sign] != 1)
    {
        s.push(data[max_sign]);
        max_sign = before[max_sign];
    }
    //将其输出
    cout << data[max_sign];
    while(!s.empty())
    {
        cout << " " << s.top();
        s.pop();
    }
}

备注

其间最关键的一步是 if(data[tmp] > data[tmp2] && sign[tmp2] + 1 > sign[tmp]) 这句(位于24行)。
在这里插入图片描述剩下三种子序列可以通过更改这段达到
最长递减子序列—— if(data[tmp] < data[tmp2] && sign[tmp2] + 1 > sign[tmp]) ——dp[a] = max{dp[n] + 1}(a < n && D(a) > D(n))
最长不下降子序列—— if(data[tmp] >= data[tmp2] && sign[tmp2] + 1 > sign[tmp])——dp[a] = max{dp[n] + 1}(a >= n && D(a) > D(n))
最长不上升子序列—— if(data[tmp] >= data[tmp2] && sign[tmp2] + 1 > sign[tmp])——dp[a] = max{dp[n] + 1}(a <= n && D(a) > D(n))
(中间是替换语句,后面是动态方程)

题目(2)

时间限制: 1000 ms
内存限制: 64 MB
代码长度限制: 16 KB
在这里插入图片描述

简单分析

1.分析与上题区别:不用输出路径,且数据量变大了,因此我们不用保留上个结点的位置,且Θ(n2)的算法是行不通的。
2.由于不用保留路径,所以我们可以开一个数组保存上题sign值相同的结点的最小值,如果后面的数比这个最小值要小,那以他为结点的子序列长度肯定大于最小值的sign。
3.遍历sign的时候采取二分法,可以时间复杂度降为Θ(nlog2n)

代码

#include <iostream>
#include <stack>
using namespace std;
int main()
{
    //s用于存放路径
    stack<int> s;
    //data记录序列,sign记录子序列长度相同的最小值
    //max_int存放最大子序列长度,num序列长度
    int data[100000],sign[100000];
    int max_int = 1, num;
    //输入数据并初始化sign
    cin >> num;
    for(int tmp = 0; tmp < num; tmp++)
        cin >> data[tmp];
    sign[0] = data[0];
    //遍历数组
    for(int tmp = 1; tmp < num; tmp++)
    {
        int high = max_int-1, low = 0;
        //先与0和max_int这两个特例进行比较
        if(sign[0] > data[tmp])
            sign[0] = data[tmp];
        else if(sign[max_int-1] < data[tmp])
        {
            sign[max_int] = data[tmp];
            max_int++;
        }
        //二分遍历side,找出tmp的位置并替换对应变量
        else
        {
            while((high - low) > 1)
            {
                if(sign[(high + low)/2] > data[tmp])
                    high = (high + low)/2;
                else
                    low = (high + low)/2;
            }
            if(sign[low] > data[tmp])
                sign[low] = data[tmp];
            else if(sign[high] > data[tmp])
                sign[high] = data[tmp];
        }
    }
    cout <<  max_int <<endl;
}

备注

偷个小懒,照着第一题的改法,改掉for循环内的所有比较的符号就行了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态规划是一种解决最值问题的方法,其中最递增序列问题是其中的一个典型应用。在这个问题中,我们需要找到一个给定序列中最递增序列动态规划解决最递增序列问题的一般步骤如下: 1. 创建一个度与原始序列相同的dp数组,用来存储以当前元素为结尾的最递增序列度。 2. 初始化dp数组的每个元素为1,表示以该元素为结尾的最递增序列度至少为1。 3. 从序列的第二个元素开始,遍历整个序列。 4. 对于每个元素,再次遍历该元素之前的所有元素。 5. 如果当前元素大于前面的某个元素,并且以该前面的元素为结尾的最递增序列度加1大于当前元素所在位置的dp数组的值,则更新当前元素的dp数组的值为该度。 6. 最后,遍历整个dp数组,找到最大值,即为最递增序列度。 根据上述步骤,我们可以使用普通的动态规划算法来解决最递增序列问题,其时间复杂度为O(N^2)。引用介绍了动态规划方法在解决最递增序列问题时的应用。引用指出了在解决该问题时,需要按照第一维参数进行升序排序,然后按照第二维参数降序排序。而引用中的代码展示了如何利用dp数组来求得最递增序列度。 因此,根据上述引用内容,可以总结出通过动态规划来解决最递增序列问题的一般方法和相关注意事项。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [动态规划算法 | 最递增序列](https://blog.csdn.net/qq_38844835/article/details/127464625)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值