【刷题记录】排列子序列


1️⃣题目

在这里插入图片描述

注意:题目中的关键信息 ——子序列是连续且非递增或非递减的、最少分为多少组。


2️⃣思路

若有一个序列a如下:

int a[] = {1,2,3,6,5,4,0,1};

根据题意,我们要把该序列分成以下几组,得出组数
在这里插入图片描述

当然也可能会有如下情况,子序列中有重复项,这是我们需要特殊注意的点:
在这里插入图片描述

💡总体思路:定义一个计数变量cnt和一个标志变量flag,flag为1时表示当前子序列为非递减序列,为0时表示当前子序列为非递增序列。遍历数组,判断当前数组元素是否满足flag标识的序列状态,若不满足,则cnt加1(当前元素前面的序列就是一段排序子序列),然后更新flag。重复上述过程,最终就能得到最少的排序子序列组数。

⭕更新flag的过程要尤其注意,flag的值需根据每个子序列前两个不同的元素来判断,由于每个子序列并不是单纯的递增或递减,可能包含相邻重复的数,因此我们需要去重后才能判断flag的更新值。

就像这个序列:
2222222222222222222222222210
我们需要用元素21来判断出它是非递增序列。


3️⃣代码分析

💬代码如下

时间复杂度:O(N)
空间复杂度:O(1)

#include <iostream>

using namespace std;

int find_next(long long* a,long long n,int i)
{
    // 去重,找到下一个不一样的
    int j = i+1;
    while(j < n && a[j] == a[i])
    {
        ++j;
    }
    return j;
}

int main()
{
    // 输入
    long long n = 0;
    cin >> n;
    long long a[n];
    for(size_t i = 0;i<n;++i)
    {
        cin >> a[i];
    }
   
    // 设置第一个子序列的flag值,1是非递减,0是非递增
    int next = find_next(a,n,0); // next表示当前数组位置去重后的下一位置
    int flag = 1; // 假设第一个子序列的flag为1
    if(next < n && a[next] < a[0])
    {
        flag = 0; // 不满足,则置0
    }
    
    long long cnt = 0;
    for(size_t i = next;i<n;++i)
    {
        if(flag == 1 && a[i] < a[i-1]) // 前面的序列是非递减序列
        {
            ++cnt;
            // 更新flag
            next = find_next(a,n,i);
            if(next < n && a[next] < a[i])
            {
                flag = 0;
            }
            i = next; // 下一次循环便跳过了当前子序列的前两个元素
        }
        else if(flag == 0 && a[i] > a[i-1]) // 前面的序列是非递增序列
        {
            ++cnt;
            // 更新flag
            next = find_next(a,n,i);
            if(next < n && a[next] > a[i])
            {
                flag = 1;
            }
            i = next;
        }
    }
    //最后的结果是cnt+1,因为最后一个子序列在循环中没有被计入
    cout << cnt+1 << endl;
    return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值