当前位置: 首页 > article >正文

AT_abl_d 题解

线段树优化 DP 的板子。

题意

给定一个数列,求出它最长的子序列,满足相邻元素之差不大于 k k k

分析

对于这类最长子序列的题目,有一个 DP 套路,设 d p i dp_i dpi 表示以 a i a_i ai 结尾的最长合法子序列长度,则有

d p i = max ⁡ j = 1 j < i d p j + 1 dp_i=\max_{j=1}^{j<i} dp_j+1 dpi=j=1maxj<idpj+1

其中, ∣ a i − a j ∣ ≤ k |a_i-a_j| \le k aiajk

Code:

#include <bits/stdc++.h>
#define N 300005
using namespace std;

int n, k, a[N], dp[N];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> k; for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        dp[i] = 1;
        for (int j = 1; j < i; j++) {
            if (abs(a[i] - a[j]) <= k) dp[i] = max(dp[i], dp[j] + 1);
        }
    }
    cout << *max_element(dp + 1, dp + n + 1);
    return 0;
}

喜提 TLE。

显然,上述思路的复杂度为 O ( n 2 ) O(n^2) O(n2),考虑优化。

考虑下面的过程:

for (int j = 1; j < i; j++) {
    if (abs(a[i] - a[j]) <= k) dp[i] = max(dp[i], dp[j] + 1);
}

什么样的 j j j 可以转移呢?

当且仅当 a j ∈ [ a i − k , a i + k ] a_j \in [a_i-k, a_i+k] aj[aik,ai+k] 时,可以进行转移。此时,题目被转化成了一个 RMQ 问题,需要单点修改,区间查询最值,想到线段树。

AC Code 如下,注意边界条件。

#include <bits/stdc++.h>
#define N 300005
using namespace std;

int n, k, a[N];

template <class T>
class SegmentTree {
#define ls (rt << 1)
#define rs (rt << 1 | 1)
private:
    int n;
    T maxn[(N << 2) + 1];
    
    void pushup(int rt) {
        maxn[rt] = max(maxn[ls], maxn[rs]);
    }
    
    void build(int l, int r, int rt) {
        if (l == r) return (void) (maxn[rt] = 0);
        int mid = (l + r) >> 1;
        build(l, mid, ls), build(mid + 1, r, rs);
        pushup(rt);
    }
    
    void update(int i, const T c, int l, int r, int rt) {
        if (l == r) return (void) (maxn[rt] = max(maxn[rt], c));
        int mid = (l + r) >> 1;
        if (i <= mid) update(i, c, l, mid, ls);
        else update(i, c, mid + 1, r, rs);
        pushup(rt);
    }
    
    T query(int tl, int tr, int l, int r, int rt) {
        if (tl <= l && r <= tr) return maxn[rt];
        int mid = (l + r) >> 1; T res(0);
        if (tl <= mid) res = max(res, query(tl, tr, l, mid, ls));
        if (tr > mid) res = max(res, query(tl, tr, mid + 1, r, rs));
        return res;
    }
    
public:
    explicit SegmentTree(int _n) : n(_n) {build(1, n, 1);}
    void update(int i, const T c) {update(i, c, 1, n, 1);}
    T query(int l, int r) {return query(l, r, 1, n, 1);}
    
#undef ls
#undef rs
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> k; for (int i = 1; i <= n; i++) cin >> a[i];
    int m = *max_element(a + 1, a + n + 1);
    SegmentTree<int> sgt(m);
    for (int i = 1; i <= n; i++) {
        sgt.update(a[i], sgt.query(max(1, a[i] - k), min(m, a[i] + k)) + 1);
    }
    cout << sgt.query(0, m);
    return 0;
}

线段树优化后,复杂度为 O ( n log ⁡ w ) O(n \log w) O(nlogw),其中 w w w a i a_i ai 的值域。可以通过本题。


http://www.kler.cn/news/232710.html

相关文章:

  • Java基础常见面试题总结-并发(二)
  • 淘宝镜像到期如何切换镜像及如何安装淘宝镜像
  • Git版本与分支
  • IPMI命令
  • 元宇宙虚拟数字人实训室:推动高校培养创新技术人才
  • 【每日一题】LeetCode——链表的中间结点
  • Python:批量url链接保存为PDF
  • 智能运维哪些算法?智能运维包含哪些
  • 多模态对比语言图像预训练CLIP:打破语言与视觉的界限,具备零样本能力
  • [Vue3]父子组件相互传值数据同步
  • Redis发布订阅及事务管理
  • docker常用10条容器操作命令
  • 阿里 EasyExcel 表头国际化
  • Vue3——模板语法(文本插值、vue内置指令)
  • Vue 前置导航
  • OpenHarmony轻量级内核-LiteOS-M
  • final、finally、finalize区别
  • 8个简约精美的WordPress外贸网站主题模板
  • 编码技巧——基于RedisTemplate的RedisClient实现、操作Lua脚本
  • CentOS 安装 redis 7.2
  • 使用Launch4j将jar包转成.exe可执行文件
  • OCR文本纠错思路
  • C语言中的多级指针、指针数组与数组指针
  • 飞天使-k8s知识点15-kubernetes散装知识点4-CNI网络插件与kubectl
  • 【Git版本控制 01】基本操作
  • MinIO数据迁移
  • 【前端web入门第四天】01 复合选择器与伪类选择器
  • 代码随想录算法——数组
  • 2024年华为OD机试真题-螺旋数字矩阵-Java-OD统一考试(C卷)
  • scss和less的区别