离散化

简介: 复习acwing算法基础课的内容,本篇为讲解基础算法:离散化,关于时间复杂度:目前博主不太会计算,先鸽了,日后一定补上.

文章目录

前言

一、有关离散化

二、离散化例题,模板,代码详解

1.例题:AcWing 802. 区间和

2.例题分析:

遍历add数组:

find 函数:

处理前缀和数组:

3.例题模板

4.AC代码

三、时间复杂度


前言

复习acwing算法基础课的内容,本篇为讲解基础算法:离散化,关于时间复杂度:目前博主不太会计算,先鸽了,日后一定补上.


一、有关离散化

什么时候我们会用离散化去解题:例如存在这么一条 x轴,这条 x轴十分的长,例如≥1e7级别,这条x轴上有一些数,但是这些数十分的分散,一个1e8长度的轴上可能只有1e3个数字,我们如果想求这条数轴上任意两点之间所有数字之和的话,如果把这条 x轴设成一个数组,然后利用for去遍历累加的话,那么我们需要开一个a[1e8]长度的数组,这样显然是不合理的。在这种情况下我们就可以采用离散化这一算法去解题。


离散化的本质就是映射,就是把那些散点(间隔很大的点),映射到一个数组中,比如一个a[100]的数组中,只有a[1] = 2, a[64] = 9,其余的点都是0,那么对于这么一个数组,我们可以通过离散化开一个新的数组b[3],使得b[1] = 2, b[2] = 9,这样就省去了很多的空间,同时也省去了for循环遍历数组时的计算量,这里直接上例题去讲解


二、离散化例题,模板,代码详解

1.例题:AcWing 802. 区间和

本题链接:AcWing 802. 区间和

本博客给出例题截图:

image.png

image.pngimage.png

2.例题分析:

本题的坐标轴为 [-1e-9, 1e9],显然我们开个a[2e9]的数组是不合理的,而且这些坐标中最多也只有1e5个点,符合离散化算法的条件,本题的离散化利用了vector,关于有关vector的用法即介绍,见:STL—vector

首先来看要求[l, r]上的和,对于这一步的处理我们可以用前缀和去处理,前缀和的讲解在这篇博客:前缀和,所以我们需要一个前缀和数组s[N],同时我们需要一个储存离散化后的点的数组a[N],我们用vector去存储未离散化的坐标,往离散化前的坐标轴上加入一个值用pair类型的vector数组add去存储,关于pair的用法即介绍,见:STL—pair,同样用一个pair类型的vector数组query去存储要计算的[l, r]区间和的端点 l 和 r,然后把所有未离散化的坐标都存入到int类型的vector数组alls,那么alls里就是待离散化的所有坐标,全部处理结束后,我们需要对alls数组排序并去重,本题例子中的alls数组中存储的值为{1, 3, 7, 1, 3, 4, 6, 7, 8},这个就是本题涉及的所有坐标(未离散化),排序后为{1, 1, 3, 3, 4, 6, 7, 7, 8},显然我们对于一个坐标点只需要存储一次即可,故去重,关于本题排序并去重的代码如下图所示:(背过即可)


本代码板块来源:AcWing算法基础课

  sort(alls.begin(), alls.end());               
    alls.erase(unique(alls.begin(), alls.end()), alls.end()); 

处理完之后alls数组中的值为{1, 3, 4, 6, 7, 8},时刻都要记住本vector数组alls存储的是未离散化后的值,接下来,处理离散化,例如:

遍历add数组:

   for (auto it : add)
    {
        int x = find(it.first);         //it.first存储的是add函数中的坐标
                        //find函数返回的是it.first离散化后的坐标
        a[x] += it.second;
    }

find 函数:

查找一个数,我们可以利用之前说到的二分查找法,不懂的可以看这篇博客:二分法

本代码板块来自于AcWing算法基础课

  // 二分求出x对应的离散化的值
  int find(int x) // 找到第一个大于等于x的位置
  {
      int l = 0, r = alls.size() - 1;
      while (l < r)
      {
          int mid = l + r >> 1;
          if (alls[mid] >= x) r = mid;         //因为是 r = mid 所以 mid = l + r >> 1;
          else l = mid + 1;
      }
      return r + 1; // 映射到1, 2, ...n
      //因为要利用前缀和数组,所以返回值从1开始
  }

处理前缀和数组:

关于前缀和数组,可以看这篇博客:前缀和

    for (int i = 1; i <= alls.size(); i ++ ) s[i] = s[i - 1] + a[i];

3.例题模板

本代码模板来自于AcWing算法基础课,ACWing网站链接:AcWing

vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end());   // 去掉重复元素
// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{
    int l = 0, r = alls.size() - 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;        //因为这里是r = mid,所以mid = l + r >> 1;
        else l = mid + 1;
    }
    return r + 1; // 映射到1, 2, ...n
}

4.AC代码

这里解释一下为什么N = 300010,因为给 x轴上任意一个点加上一个值最多n次操作,所以在这一操作中最多用到1e5个坐标,之后有m次操作,每次操作要输入两个坐标 l 和 r,所以这一步操作中最多用到2e5个坐标,故一共要用到最多的坐标数量就是3e5,故开N = 300010;

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 300010;
vector<int> alls;
vector<PII> add, query;
int a[N], s[N];
int find(int x)
{
    int l = 0, r = alls.size() - 1;
    while (l < r)
    {
        int mid = l + r  + 1 >> 1;
        if (alls[mid] <= x) l = mid;     //这里是 l = mid,所以 mid = l + r + 1 >> 1;
        else r = mid - 1;
    }
    return l + 1;
}
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i ++ )
    {
        int x, c;
        scanf("%d%d", &x, &c);
        add.push_back({x, c});
        alls.push_back(x);
    }
    for (int i = 0; i < m; i ++ )
    {
        int l, r;
        scanf("%d%d", &l, &r);
        query.push_back({l, r});
        alls.push_back(l);
        alls.push_back(r);
    }
    sort(alls.begin(), alls.end());
    alls.erase(unique(alls.begin(), alls.end()), alls.end()); 
    for (auto it : add)
    {
        int x = find(it.first);
        a[x] += it.second;
    }
    for (int i = 1; i <= alls.size(); i ++ ) s[i] = s[i - 1] + a[i];
    for (auto it : query)
    {
        int l = find(it.first), r = find(it.second);
        printf("%d\n", s[r] - s[l - 1]);
    }
    return 0;
}

三、时间复杂度

关于离散化的时间复杂度以及证明,后续会给出详细的说明以及证明过程,目前先鸽了。

目录
相关文章
|
机器学习/深度学习 算法 数据可视化
浅析特征数据离散化的几种方法(上)
什么是离散化? 离散化就是把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小。例如:
|
6月前
|
人工智能 算法 C++
c++算法学习笔记 (11) 离散化
c++算法学习笔记 (11) 离散化
|
6月前
|
算法 数据挖掘 数据处理
超实用!五种常用的多离散化小技巧
超实用!五种常用的多离散化小技巧
235 0
|
6月前
|
机器学习/深度学习 算法 数据挖掘
K-均值算法
K-均值算法是发现给定数据集的k个簇的算法。簇个数k是用户给定的,每一个簇通过其簇中所有点的中心点来描述 工作流程: 首选选取样本中k个样本作为每个簇的簇中心 然后对每一个样本与每个簇之间的关系,来分配到每一个簇中 然后更新每个簇的均值
58 1
为什么进行线性回归前需要对特征进行离散化处理?
为什么进行线性回归前需要对特征进行离散化处理?
208 1
|
机器学习/深度学习 决策智能
矩阵分析 (七) 矩阵特征值的估计
矩阵分析 (七) 矩阵特征值的估计
167 0
|
算法
离散化算法
离散化算法
70 0
|
机器学习/深度学习 算法 数据挖掘
聚类练习:对地理数据应用二分k-均值算法聚类
聚类练习:对地理数据应用二分k-均值算法聚类
259 0
聚类练习:对地理数据应用二分k-均值算法聚类