【刷题汇总 -- 排序子序列、消减整数、最长上升子序列(二)】

今日刷题汇总 - day026

1、排序子序列

1.1、题目

在这里插入图片描述

1.2、思路

读完题知道,让把一个长度为n的数组划分为排序子序列,其中定义排序子序列为一个数组中一段连续的子序列,并且这段子序列是非递增或者非递减排序的。求最少可以划分多少个排序子序列。
那么,根据题意和示例分析,既然要划分最少的情况,那么直接把连续上升的区间加上相等的都划分为一个上升区间,把连续下降的区间加上相等的区间也规划为一个区间,最后利用ret计数区间即可,为了直观理解画个图:
在这里插入图片描述
接下来,就是程序实现。

1.3、程序实现

首先,按照题目要求写好输入,然后利用下标 i 遍历,如果相邻的元素是存在上升或下降的且相等的,那么直接i++直到遇到转折,再比较下一组相邻元素同理,同时ret遇到转折就ret++,最后遍历到i = n-1时为最后一个元素,就不用再与后面相邻元素比较了,直接ret++结束即可。值得注意的是,刚开始序列处于相等的情况就只需要i++,而ret需要等待上升转下降或者下降转升序的转折时才++。

#include <iostream>
using namespace std;

const int N = 1e5 + 10;
int arr[N];

int main()
{
    int n;
    cin >> n;
    for(int i = 0;i < n;i++)
        cin >> arr[i];
    
    int ret = 0;
    int i = 0;
    while(i < n)
    {
        if(i == n-1)//遍历到最后一个元素了
        {
            ret++;
            break;
        }
        //一开始不相等
        if(arr[i] < arr[i+1])
        {
            //升序且相等就划为一个序列
            while(i + 1 < n && arr[i] <= arr[i+1])
                i++;
            //遇见转折ret++
            ret++;
        }
        else if(arr[i] > arr[i+1])
        {
            //降序且相等就划为一个序列
            while(i + 1 < n && arr[i] >= arr[i+1])
                i++;
            //遇见转折ret++
            ret++;
        }
        else//一开始就相等,就只管i++即可
        {
            while(i + 1 < n && arr[i] == arr[i+1])
                i++;
        }
        //继续遍历
        i++;
    }
    cout << ret << endl;
    return 0;
}

在这里插入图片描述

2、消减整数

2.1、题目

在这里插入图片描述

2.2、思路

读完题直到,让处理一个整数,让其作减法,最终使其恰好减为0,其中每次减的数字都必须和上一次相同或者是上一次的两倍,求最少需要几次能把H恰好减到0。那么根据题目和示例分析,想到蛮力法就是枚举所有减法的可能统计执行的次数,但是对于数据量太大时会超时,所以结合贪心思想很容易想到需要尽可能多的利用减去上个数字的两倍的方案,但是题目中要求需要最终恰好减为0,那么就存在不能为0的情况,比如8 ,第一次只能减1,变成7,然后贪心减两倍,7-2=5,再贪心,5-22 = 1,那么此时1无论如何也无法通过减去4或42变成0,所以需要对贪心进行进一步的思考,既然要求减去两倍,恰好变成0,那么对于数字8,先执行第一次减1,8-1=7,然后判断7是否满足7 % 2*1 = 0?,如果等于0,则执行减2倍的方案,否则执行减上一次相同数字即可。那么接下来,就是程序实现。

2.3、程序实现 – 贪心策略

首先,按照题目要求写好输入,然后为了逻辑分明,程序健壮性封装一个func函数执行统计操作的次数,然后func中,按照思路分析对于整数,判断能否满足贪心策略,能则减2倍,否则减上一次的数即可,直到h减恰好减到0结束。

#include <iostream>
using namespace std;

int func(int h)
{
    int count = 0;//执行次数
    int a = 1;//初始减数 1
    while(h)
    {
        h -= a;
        count++;
        //判断能否执行贪心策略,下一次减2倍,否则还是减上次的数
        if(h % (2*a) == 0)
        {
            a = 2*a;
        }
    }
    return count;
}

int main()
{
    int T;
    cin >> T;
    int H;
    while(T--)
    {
        cin >> H;
        cout << func(H) << endl;
    }
    return 0;
}

在这里插入图片描述
在这里插入图片描述

3、最长上升子序列(二)

3.1、题目

在这里插入图片描述

3.2、思路

读完题知道,给定一个长度为 n 的数组a,求它的最长严格上升子序列的长度。所谓子序列,指一个数组删掉一些数(也可以不删)之后,形成的新数组。例如 [1,5,3,7,3] 数组,其子序列有:[1,3,3]、[7] 等。但 [1,6]、[1,3,5] 则不是它的子序列。其中,定义一个序列是 严格上升 的,当且仅当该序列不存在两个下标 i 和 j 满足 i < j 且ai > aj。根据题目和示例分析,要满足子序列最长,那么贪心策略尽可能要求序列按照越接近越相邻的规则存放才会最长,为了方便理解画个图:
在这里插入图片描述
那么接下来,就是程序实现。

3.3、程序实现-- 贪心策略+二分

class Solution
{
  public:
    int dp[100010] = { 0 }; // dp[i] 表⽰:⻓度为 i 的最⼩末尾
    int pos = 0;
    int LIS(vector<int>& a)
    {
        for (auto x : a)
        {
            // 查找 x 应该放在哪个位置
            if (pos == 0 || x > dp[pos]) 
            {
                dp[++pos] = x;//pos == 0,dp[1]长度为1,末尾为x;
                //x > dp[pos]
                //对于最大元素,就增加到末尾,长度+1
            } 
            else
            {
                // ⼆分查找插⼊位置
                int left = 1, right = pos;
                while (left < right)
                {
                    int mid = (left + right) / 2;
                    if (dp[mid] >= x)
                        right = mid;
                    else 
                        left = mid + 1;
                }
                //插入的位置
                dp[left] = x;
            }
        }
        return pos;

    }
};

在这里插入图片描述

4、题目链接

🌟排序子序列
🌟消减整数
🌟最长上升子序列(二)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值