题解:学而思编程 递增

【题目来源】

学而思编程:递增

【题目描述】

现有数列 \(A_1,A_2,⋯ ,A_N\) 修改最少的数字(可以修改成小数),使得数列严格单调递增。

【输入】

\(1\) 行,\(1\) 个整数 \(N\)

\(2\) 行,\(N\) 个整数 \(A_1,A_2,⋯ ,A_N\)

【输出】

\(1\) 个整数,表示最小修改的数字个数

【输入样例】

3
1 3 2

【输出样例】

1

【核心思想】

  1. 问题分析:给定长度为 \(N\) 的数列,要求修改最少的数字使得数列严格单调递增。等价于求最长上升子序列(LIS),因为保留的最多不修改的元素就是 LIS,需要修改的元素个数为 \(N - \text{LIS长度}\)

  2. 算法选择

    • 贪心 + 二分优化:维护数组 \(b\)\(b[i]\) 表示长度为 \(i\) 的上升子序列的最小结尾元素
    • lower_bound:使用二分查找找到第一个大于等于 \(a[i]\) 的位置
  3. 关键步骤

    • 初始化\(len = 0\)\(b[0]\) 为极小值
    • 对于每个元素 \(a[i]\)
      • \(a[i] > b[len]\),则 \(b[++len] = a[i]\),扩展 LIS
      • 否则,用 lower_bound 找到位置 \(m\),更新 \(b[m] = a[i]\),保持最小性
    • 答案\(N - len\),总元素数减去 LIS 长度
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log N)\),每个元素二分查找
    • 空间复杂度:\(O(N)\),数组 \(b\)\(f\)
  5. LIS 贪心优化的核心思想

    • 转化思想:最少修改次数 = 总元素数 - 最长不修改的元素个数(LIS)
    • 贪心策略:同样长度的子序列,结尾越小越有利于后续扩展
    • 二分查找:利用 \(b\) 数组的单调性,用 lower_bound 快速定位
    • 替换策略:用更小的值替换,保持 \(b\) 数组的最小性
    • 适用于最少修改/删除使序列递增的问题

【算法标签】

线性DP-一维

【代码详解】

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100005;
int n, a[N], b[N], f[N], maxn, len;  // b: 最长递增序列, f: 每个元素所在的LIS长度

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];  // 输入序列
    }

    for (int i = 1; i <= n; i++)
    {
        if (a[i] > b[len])  // 当前元素大于序列最后一个元素
        {
            b[++len] = a[i];  // 扩展序列
            f[i] = len;  // 记录当前元素所在的LIS长度
        }
        else
        {
            // 找到第一个大于等于a[i]的位置
            int m = lower_bound(b + 1, b + len + 1, a[i]) - b;
            b[m] = a[i];  // 替换该位置的元素
            f[i] = m;  // 记录当前元素所在的LIS长度
        }
    }
    cout << n - len;  // 输出最少需要删除的元素个数
    return 0;
}

【运行结果】

3
1 3 2
1
posted @ 2026-06-14 10:48  团爸讲算法  阅读(4)  评论(0)    收藏  举报