首页 > 其他分享 >最长递增子序列

最长递增子序列

时间:2024-10-14 21:35:11浏览次数:1  
标签:ends nums int 递增 pos len vector 序列 最长

最长递增子序列

300. 最长递增子序列

  • 普通解法
#include <vector>

using namespace std;

class Solution {
public:
    // 时间复杂度 O(n^2)
    int lengthOfLIS(vector<int> &nums) {
        int n = nums.size();
        // dp[i]: 以 nums[i] 结尾的最长递增子序列
        vector<int> dp(n);
        int res = 0;
        for (int i = 0; i < n; ++i) {
            dp[i] = 1;
            for (int j = 0; j < i; ++j) {
                if (nums[j] < nums[i])
                    dp[i] = max(dp[i], dp[j] + 1);
            }
            res = max(res, dp[i]);
        }
        return res;
    }
};
  • 最优解
#include <vector>

using namespace std;

class Solution {
public:
    // 大于等于 target 的左边界
    int binarySearch(vector<int> &ends, int len, int target) {
        int left = 0;
        int right = len - 1;
        int mid;
        while (left <= right) {
            mid = left + ((right - left) >> 1);
            if (ends[mid] >= target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

    // 时间复杂度 O(n * logn)
    int lengthOfLIS(vector<int> &nums) {
        int n = nums.size();
        // ends[i] 表示所有长度为 i + 1 的递增子序列的最小结尾
        // [0, len-1] 是有效区,有效区内的数字一定严格升序
        vector<int> ends(n);
        // len 表示 ends 数组目前的有效区长度
        int len = 0;
        for (int i = 0, pos; i < n; ++i) {
            pos = binarySearch(ends, len, nums[i]);
            if (pos == len) {
                // 找不到就扩充 ends
                ends[len++] = nums[i];
            } else {
                // 找到了就更新成更小的 nums[i]
                ends[pos] = nums[i];
            }
        }
        return len;
    }
};
  • 最长不下降子序列
#include <vector>

using namespace std;

class Solution {
public:
    // 大于 target 的左边界
    int binarySearch(vector<int> &ends, int len, int target) {
        int left = 0;
        int right = len - 1;
        int mid;
        while (left <= right) {
            mid = left + ((right - left) >> 1);
            if (ends[mid] > target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

    // 时间复杂度 O(n * logn)
    int lengthOfLIS(vector<int> &nums) {
        int n = nums.size();
        // ends[i] 表示所有长度为 i + 1 的不下降子序列的最小结尾
        // [0, len-1] 是有效区,有效区内的数字非递减
        vector<int> ends(n);
        // len 表示 ends 数组目前的有效区长度
        int len = 0;
        for (int i = 0, pos; i < n; ++i) {
            pos = binarySearch(ends, len, nums[i]);
            if (pos == len) {
                // 找不到就扩充 ends
                ends[len++] = nums[i];
            } else {
                // 找到了就更新成更小的 nums[i]
                ends[pos] = nums[i];
            }
        }
        return len;
    }
};

354. 俄罗斯套娃信封问题

#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

class Solution {
public:
    // 找大于等于的左边界
    int binarySearch(vector<int> &ends, int len, int target) {
        int left = 0;
        int right = len - 1;
        int mid;
        while (left <= right) {
            mid = left + ((right - left) >> 1);
            if (ends[mid] >= target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

    int maxEnvelopes(vector<vector<int>> &envelopes) {
        // 宽度从小到大,宽度一样,高度从大到小
        sort(begin(envelopes), end(envelopes),
             [](vector<int> &v1, vector<int> &v2) {
                 return v1[0] == v2[0] ? v1[1] > v2[1] : v1[0] < v2[0];
             });

        int n = envelopes.size();
        // ends[i] 表示长度为 i + 1 的子序列的最小末尾元素
        // 在有效区内严格递增
        vector<int> ends(n);
        // ends 数组的有效长度
        int len = 0;

        for (int i = 0, pos; i < n; ++i) {
            int target = envelopes[i][1];
            pos = binarySearch(ends, len, target);
            if (pos == len) {
                // 找不到就扩充 ends
                ends[len++] = target;
            } else {
                // 找到了就更新成更小的 nums[i]
                ends[pos] = target;
            }
        }

        return len;
    }
};

2111. 使数组 K 递增的最少操作次数

#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

class Solution {
public:
    // 大于 target 的左边界
    int binarySearch(vector<int> &ends, int len, int target) {
        int left = 0;
        int right = len - 1;
        int mid;
        while (left <= right) {
            mid = left + ((right - left) >> 1);
            if (ends[mid] > target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

    // 时间复杂度 O(n * logn)
    int lengthOfLIS(vector<int> &nums) {
        int n = nums.size();
        // ends[i] 表示所有长度为 i + 1 的不下降子序列的最小结尾
        // [0, len-1] 是有效区,有效区内的数字非递减
        vector<int> ends(n);
        // len 表示 ends 数组目前的有效区长度
        int len = 0;
        for (int i = 0, pos; i < n; ++i) {
            pos = binarySearch(ends, len, nums[i]);
            if (pos == len) {
                // 找不到就扩充 ends
                ends[len++] = nums[i];
            } else {
                // 找到了就更新成更小的 nums[i]
                ends[pos] = nums[i];
            }
        }
        return len;
    }

    int kIncreasing(vector<int> &arr, int k) {
        int n = arr.size();
        int res = 0;
        // 分为 k 组
        for (int i = 0; i < k; ++i) {
            vector<int> temp;
            for (int j = i; j < n; j += k)
                temp.emplace_back(arr[j]);
            // 累加这一组需要修改的数字
            res += temp.size() - lengthOfLIS(temp);
        }
        return res;
    }
};

646. 最长数对链

#include <vector>
#include <algorithm>

using namespace std;

class Solution {
public:
    int binarySearch(vector<int> &ends, int len, int target) {
        int left = 0;
        int right = len - 1;
        int mid;
        while (left <= right) {
            mid = left + ((right - left) >> 1);
            if (ends[mid] >= target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

    int findLongestChain(vector<vector<int>> &pairs) {
        // 按照数对中第一个数增序
        sort(begin(pairs), end(pairs),
             [](vector<int> &v1, vector<int> &v2) {
                 return v1[0] < v2[0];
             });
        int n = pairs.size();
        vector<int> ends(n);
        int len = 0;
        for (int i = 0, pos; i < n; ++i) {
            // 根据数对中第一个数字查
            pos = binarySearch(ends, len, pairs[i][0]);
            if (pos == len) {
                // 插入的是数对中的第二个数字
                ends[len++] = pairs[i][1];
            } else {
                // 改成较小的
                ends[pos] = min(ends[pos], pairs[i][1]);
            }
        }
        return len;
    }
};

P8776 [蓝桥杯 2022 省 A] 最长不下降子序列

#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

int n, k;

// 求最长不上升子序列长度的二分
// ends[0, len - 1] 为降序,找小于 target 的最左位置
int binarySearch1(vector<int> &ends, int len, int target) {
    int left = 0;
    int right = len - 1;
    int mid;
    while (left <= right) {
        mid = left + ((right - left) >> 1);
        if (ends[mid] < target) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return left;
}

// 求最长不下降子序列长度的二分
// ends[0, len-1] 为升序,找大于 target 的最左位置
int binarySearch2(vector<int> &ends, int len, int target) {
    int left = 0;
    int right = len - 1;
    int mid;
    while (left <= right) {
        mid = left + ((right - left) >> 1);
        if (ends[mid] > target) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return left;
}

// 生成辅助数组 rightMaxLen
// rightMaxLen[i]: 以 nums[i] 开头的最长不下降子序列长度
// 等价于从右往左遍历,以 nums[i] 做结尾的情况下的最长不上升子序列
vector<int> getRightMaxLen(vector<int> &ends, vector<int> &nums) {
    vector<int> rightMaxLen(nums.size());
    int len = 0;
    for (int i = n - 1, pos; i >= 0; i--) {
        pos = binarySearch1(ends, len, nums[i]);
        if (pos == len) {
            // 扩充 endsArr
            ends[len++] = nums[i];
            // 记录长度
            rightMaxLen[i] = len;
        } else {
            ends[pos] = nums[i];
            rightMaxLen[i] = pos + 1;
        }
    }
    return rightMaxLen;
}

int main() {
    cin >> n >> k;
    vector<int> nums;
    nums.resize(n);
    for (int i = 0; i < n; ++i)
        cin >> nums[i];

    // 生成辅助数组
    vector<int> ends(n);
    vector<int> rightMaxLen = getRightMaxLen(ends, nums);

    int len = 0;
    int res = 0;
    for (int i = 0, j = k, pos; j < n; i++, j++) {
        // 根据当前划分点查,划分点左侧连续 k 个位置是要改成 nums[j] 的
        pos = binarySearch2(ends, len, nums[j]);

        // res 由三部分组成
        // 左侧:划分点左侧连续 k 个位置再往前的区域中,长度为 pos 的不下降子序列(最大值小于 nums[j])
        // 中间:划分点左侧连续 k 个位置
        // 右侧:必须以 nums[j] 开始的不下降子序列的长度
        res = max(res, pos + k + rightMaxLen[j]);

        // 要插入的是 nums[i],所以要再查找下插入位置
        pos = binarySearch2(ends, len, nums[i]);
        if (pos == len) {
            ends[len++] = nums[i];
        } else {
            ends[pos] = nums[i];
        }
    }
    // 特例:最后 k 个元素都改成左侧不下降子序列的最后一个值
    res = max(res, len + k);
    cout << res;
}

标签:ends,nums,int,递增,pos,len,vector,序列,最长
From: https://www.cnblogs.com/sprinining/p/18466191

相关文章

  • 代码随想录算法训练营第三十一天|455.分发饼干 376. 摆动序列 53. 最大子序和
    455.分发饼干假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干j,都有一个尺寸s[j] 。如果s[j] >=g[i],我们可以将这个饼干j分配给孩子i,......
  • 题解:[HNOI2009] 双递增序列
    ProblemLink[HNOI2009]双递增序列题目描述给定一个长度为\(n\)的序列(\(n\)为偶数),求是否可以把序列分成\(2\)个长度为\(\frac{n}{2}\)的递增序列。Solution首先想到定义\(f_i\)为一个序列以\(a_i\)结尾时另一个序列末尾最小值。但这样定义状态信息是不够的,考虑......
  • 牛客AB33.相差不超过k的最多数 (滑动窗口) 牛客.DP最长公共子序列牛客.春游主持人调度
    目录牛客AB33.相差不超过k的最多数 (滑动窗口) 牛客.DP最长公共子序列牛客.春游主持人调度(二)牛客AB33.相差不超过k的最多数 (滑动窗口) 和之前那个空调吹风属于一道题的类型,当然滑动窗口,最大值-最小值,然后<=p即可也可以双指针来取寻找最大值和最小值impor......
  • 最长数对链的长度
    给你一个由 n 个数对组成的数对数组 pairs ,其中 pairs[i]=[lefti,righti] 且 lefti <righti 。现在,我们定义一种 跟随 关系,当且仅当 b<c 时,数对 p2=[c,d] 才可以跟在 p1=[a,b] 后面。我们用这种形式来构造 数对链 。找出并返回能够形成的 最......
  • P11187 配对序列
    P11187配对序列-洛谷|计算机科学教育新生态(luogu.com.cn)考虑DP,看注释,时间复杂度\(O(n)\)。非最优思路。#include<iostream>#include<cstdio>#include<algorithm>usingnamespacestd;constintN=500010;intf[N][2];//f[i][0/1]前i个里面的最大子序列......
  • DAY31 ||贪心算法基础 | 455.分发饼干 |376.摆动序列 |53.最大子数组和
    贪心算法基础贪心算法是一种在求解问题时采取逐步构建解决方案的算法策略。它通过在每一步选择在当前看来最优的选择(即“贪心”选择),希望通过局部最优解的累积得到全局最优解。贪心算法的核心思想局部最优:每一步都选择在当前状态下最优的选择,不考虑后续步骤可能带来的影响。......
  • DAY30||491.非递减子序列 |46.全排列 |47.全排列Ⅱ
    491.非递减子序列题目:491.非递减子序列-力扣(LeetCode)给定一个整型数组,你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。示例:输入:[4,6,7,7]输出:[[4,6],[4,7],[4,6,7],[4,6,7,7],[6,7],[6,7,7],[7,7],[4,7,7]]说明:给定数组......
  • 第2关:寻找一个序列中的第K小的元素(即第k小元问题)
    [TOC]寻找一个序列中的第K小的元素(即第k小元问题)对于给定的含有n(n<=100)元素的无序序列,求这个序列中第k(1≤k≤n)小的元素。任务描述本关任务:编写一个能计算数组中的第k小的元素的小程序。相关知识假设无序序列存放在a[0…n-1]中,若将a递增排序,则第k小的元素为a[k-1]。......
  • 第108天:免杀对抗-Python&混淆算法&反序列化&打包生成器&Py2exe&Nuitka
    知识点#知识点:1、Python-对执行代码做文章2、Python-对shellcode做文章3、Python-对代码打包器做文章#章节点:编译代码面-ShellCode-混淆编译代码面-编辑执行器-编写编译代码面-分离加载器-编写程序文件面-特征码定位-修改程序文件面-加壳花指令-资源代码加载面-Dll反......
  • idea-java序列化serialversionUID自动生成
    简介java.io.Serializable是Java中的一个标记接口(markerinterface),它没有任何方法或字段。当一个类实现了Serializable接口,那么这个类的对象就可以被序列化和反序列化。序列化是将对象的状态转换为字节流的过程,这样可以方便地将对象存储到文件中或者通过网络传输。反序列化......