离散化——Acwing.802区间和

2024-06-13 22:44
文章标签 acwing.802 离散 区间

本文主要是介绍离散化——Acwing.802区间和,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

离散化

定义

离散化可以简单理解为将连续的数值或数据转换为离散的、有限个不同的值或类别。离散化就是将一个可能具有无限多个取值或在一个较大范围内连续取值的变量,通过某种规则或方法,划分成若干个离散的区间或类别,并将原始数据映射到这些离散的类别中。

主要目的通常是为了简化数据处理、降低数据维度、提高计算效率或适应特定的算法和模型要求。离散化可以去除一些不太重要的细节信息,突出数据的主要特征和模式。

运用情况

当数据范围很大,但实际涉及到的不同值相对较少时,通过离散化可以用较小的索引或标记来代表原来的数值,从而节省空间和提高处理效率。

例如,有一组数值可能非常大(比如从 1 到 1000000),但实际上只有少数几种不同的值频繁出现,通过离散化可以将这些值映射到一个较小的范围内(比如 1 到 10)。

离散化的一个常见做法是先对原始数据进行排序,然后为每个不同的值分配一个唯一的编号或索引。这样在后续的处理中就可以用这些编号来代替具体的数值进行操作。

离散化在一些算法中,如一些基于区间或计数的问题中经常被用到,可以使问题的处理更加简洁和高效。

注意事项

  1. 要确保离散化后的数据能准确反映原始数据的关键特征和关系,不能丢失重要信息。
  2. 注意边界情况的处理,避免出现遗漏或错误分类。
  3. 对于不同的应用场景,选择合适的离散化方法,考虑数据分布特点和后续算法的需求。

离散方法

  1. 等宽离散化:将数据的取值范围划分为若干个等宽的区间,每个区间对应一个离散值。
  2. 等频离散化:将数据划分为若干个区间,使得每个区间内的数据数量大致相等。
  3. 基于聚类的离散化:利用聚类算法将数据聚成若干类,然后将这些类作为离散化后的类别。
  4. 基于特定规则的离散化:根据具体问题和业务需求,人为设定一些规则来进行离散化,比如根据某个阈值进行划分。
  5. 基于决策树的离散化:通过构建决策树,根据节点分裂的情况来确定离散化的划分点。

解题思路

  1. 分析问题:确定是否适合使用离散化,以及离散化的目的是什么。
  2. 选择方法:根据数据特点和问题需求选择合适的离散化方法,如等宽、等频等。
  3. 处理数据:按照选定的方法对数据进行离散化操作,建立映射关系。
  4. 验证效果:检查离散化后的数据在后续处理或分析中是否达到预期效果,必要时进行调整。
  5. 结合算法:考虑离散化后如何与具体的算法或模型相结合,使其更好地发挥作用。

例如,在处理一些区间相关的问题时,可能先对区间的端点进行离散化,然后根据离散化后的索引进行计算和处理;或者在数据量很大但实际不同值有限的情况下,通过离散化来提高存储和计算效率。同时,在解题过程中要不断思考如何优化离散化的过程以及更好地利用离散化后的结果。

Acwing.802区间和

题目描述

802. 区间和 - AcWing题库

运行代码

#include<bits/stdc++.h>  
using namespace std;  
typedef pair<int, int> PII;  
const int N = 300010;  
PII add[N];  
PII query[N];  
int a[N], s[N];  
vector<int> alls;  
int find(int x) {  return lower_bound(alls.begin(), alls.end(), x) - alls.begin() + 1;  
}  int main() {  int n, m;  cin >> n >> m;  // 读取修改操作并存储到alls中  for (int i = 0; i < n; i++) {  cin >> add[i].first >> add[i].second;  alls.push_back(add[i].first);  }  // 读取查询操作并存储到alls中  for (int i = 0; i < m; i++) {  cin >> query[i].first >> query[i].second;  alls.push_back(query[i].first);  alls.push_back(query[i].second);  }  // 离散化  sort(alls.begin(), alls.end());  alls.erase(unique(alls.begin(), alls.end()), alls.end());  // 根据离散化后的位置更新a数组  for (int i = 0; i < n; i++) {  int pos = find(add[i].first);  a[pos] += add[i].second;  }  // 计算前缀和  for (int i = 1; i <= alls.size(); i++) {  s[i] = s[i - 1] + a[i];  }  // 处理查询  for (int i = 0; i < m; i++) {  int l = find(query[i].first);  int r = find(query[i].second);  cout << s[r] - s[l - 1] << endl;  }  return 0;  
}

代码思路

  1. 读取输入
    • 读取修改操作的个数 n 和查询操作的个数 m
    • 读取每个修改操作,包括修改的位置 x 和增加的值 c,并将位置 x 存储在 add 数组中。
    • 读取每个查询操作,包括查询的左边界 l 和右边界 r,并将它们存储在 query 数组中。
  2. 离散化:将所有修改和查询中涉及到的位置(即 add 和 query 数组中的 xl 和 r)存储到 alls 向量中。对 alls 向量进行排序,并使用 unique 函数去除重复元素,实现离散化。
  3. 更新数组:遍历 add 数组,对于每个修改操作,使用 find 函数找到离散化后的位置 pos,并更新 a[pos] 数组的值。
  4. 计算前缀和:遍历离散化后的位置数组 alls(从索引 1 开始,因为索引 0 不代表任何位置),计算 a 数组的前缀和,并将结果存储在 s 数组中。
  5. 处理查询
    • 遍历 query 数组,对于每个查询操作,使用 find 函数找到离散化后的左边界 l 和右边界 r 的位置。
    • 输出区间和 s[r] - s[l - 1],其中 s[r] 是右边界的前缀和,s[l - 1] 是左边界左边一个位置的前缀和(用于排除左边界本身)。
  6. 输出结果:对于每个查询操作,输出计算得到的区间和。

这个代码的时间复杂度主要由离散化、更新数组、计算前缀和以及处理查询四部分组成。其中,离散化和计算前缀和的时间复杂度都是 O(n log n),其中 n 是修改和查询操作的总数(因为 alls 的大小可能达到这个数量级)。更新数组和处理查询的时间复杂度都是 O(n),所以整体的时间复杂度是 O(n log n)。

改进思路

  1. 减少alls数组中的冗余元素:在将位置加入alls数组时,如果两个修改或查询操作的位置相同,我们只需要存储一个。这可以通过在加入新位置之前检查alls数组的末尾元素是否与新位置相同来实现。

  2. 使用std::vectorback_inserter:在将位置加入alls数组时,使用std::back_inserter可以避免显式地调用push_back,这可能会稍微提高性能。

  3. 使用std::lower_bound的返回值直接作为索引:在find函数中,我们可以直接使用std::lower_bound返回的迭代器减去alls.begin()得到离散化后的索引,而不是将其转换为整数。

改进代码(AI)

#include<bits/stdc++.h>  
using namespace std;  
typedef pair<int, int> PII;  
const int N = 300010;  PII add[N];  
PII query[N];  
int a[N], s[N];  
vector<int> alls;  int find(int x) {  return lower_bound(alls.begin(), alls.end(), x) - alls.begin() + 1;  
}  int main() {  int n, m;  cin >> n >> m;  // 读取修改操作  for (int i = 0; i < n; i++) {  cin >> add[i].first >> add[i].second;  if (alls.empty() || alls.back() != add[i].first) alls.push_back(add[i].first);  }  // 读取查询操作  for (int i = 0; i < m; i++) {  cin >> query[i].first >> query[i].second;  if (alls.empty() || alls.back() != query[i].first) alls.push_back(query[i].first);  if (alls.empty() || alls.back() != query[i].second) alls.push_back(query[i].second);  }  // 离散化  sort(alls.begin(), alls.end());  alls.erase(unique(alls.begin(), alls.end()), alls.end());  // 根据离散化后的位置更新a数组  for (int i = 0; i < n; i++) {  int pos = find(add[i].first);  a[pos] += add[i].second;  }  // 计算前缀和  for (int i = 1; i <= alls.size(); i++) {  s[i] = s[i - 1] + a[i];  }  // 处理查询  for (int i = 0; i < m; i++) {  int l = find(query[i].first);  int r = find(query[i].second);  cout << s[r] - s[l - 1] << endl;  }  return 0;  
}

这篇关于离散化——Acwing.802区间和的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1058632

相关文章

AI学习指南机器学习篇-朴素贝叶斯处理连续特征和离散特征

AI学习指南机器学习篇-朴素贝叶斯处理连续特征和离散特征 在机器学习领域,朴素贝叶斯是一种常用的分类算法,它的简单性和高效性使得它在实际应用中得到了广泛的应用。然而,在使用朴素贝叶斯算法进行分类时,我们通常会面临一个重要的问题,就是如何处理连续特征和离散特征。因为朴素贝叶斯算法基于特征的条件独立性假设,所以对于不同类型的特征,我们需要采取不同的处理方式。 在本篇博客中,我们将探讨如何有效地处理

秋招突击——6/22——复习{区间DP——加分二叉树,背包问题——买书}——新作{移除元素、实现strStr()}

文章目录 引言复习区间DP——加分二叉树个人实现 背包问题——买书个人实现参考实现 新作移除元素个人实现参考思路 找出字符串中第一个匹配项的下标个人实现参考实现 总结 引言 今天做了一个噩梦,然后流了一身汗,然后没起来,九点多才起床背书。十点钟才开始把昨天那道题题目过一遍,然后十一点才开始复习题目,为了不耽误下午的时间,所以这里的就单纯做已经做过的题目,主打一个有量,不在学

读线圈和离散状态寄存器信息

一.功能码操作类型 二.读线圈状态 需求实例 读取设备地址为 3 的从设备的线圈状态寄存器,线圈地址为 19 到 55(从 0 开始计算)共 37 个状态。 分析:由需求可知读取地址,则功能码是0x01,地址为3即为0x03,线圈地址为19到55则起始地址为19,即0x13,数量为37,即是0x25,查询报表如下所示: 假设从设备的状态值如下: 对应的响应包如下: 使

【机器学习】基于Softmax松弛技术的离散数据采样

1.引言 1.1.离散数据采样的意义 离散数据采样在深度学习中起着至关重要的作用,它直接影响到模型的性能、泛化能力、训练效率、鲁棒性和解释性。 首先,采样方法能够有效地平衡数据集中不同类别的样本数量,使得模型在训练时能够更均衡地学习各个类别的特征,从而避免因数据不平衡导致的偏差。 其次,合理的采样策略可以确保模型在训练过程中能够接触到足够多的样本,避免过拟合和欠拟合问题,提高模型的泛化能力

算法基础精选题单 动态规划(dp)(区间dp)(个人题解)

目录 前言: 正文:   题单:【237题】算法基础精选题单_ACM竞赛_ACM/CSP/ICPC/CCPC/比赛经验/题解/资讯_牛客竞赛OJ_牛客网 (nowcoder.com) NC50493 石子合并: NC50500 凸多边形的划分: NC235246 田忌赛马: NC13230 合并回文子串: NC16645 [NOIP2007]矩阵取数游戏: NC207781 迁徙

[POJ 3190] Stall Reservations (区间贪心)

POJ - 3190 给定若干个区间,问至少要分成几组 使得同组的区间互不重叠 典型的区间贪心问题 贪心的策略就是对左端点排序,然后依次选择安排 记录一下每个隔间最右端点的位置,然后用最小堆维护一下 当前区间尽可能地放到最右点最小的组里 如果这组依旧放不进去,就没有隔间能放得进去了 所以就要为其申请一个新的隔间 否则就把它安排到这个隔间里,并且更新此隔间最右端点 #p

[POJ 1328] Radar Installation (区间贪心)

POJ - 1328 给定若干个 x轴上方的点,要求最少的圆,使得每个点都被圆覆盖 其中圆心在 x轴上,半径为 D 有一个很直接的贪心思路,就是先考虑最左边未覆盖的点 在覆盖它的情况下,尽量把圆向左移。 这个做法是错误的,因为圆并不是矩形,例如以下数据 Input: 2 3 0 0 1 3 Output: 1 正确的做法是预处理出覆盖每个点的圆心的范围 然

[POJ 2376] Cleaning Shifts (区间贪心)

POJ - 2376 给定一个区间,要求用最少的区间将其覆盖 典型的区间贪心问题 首先将区间按左端点排序,然后考虑覆盖区间最左未覆盖位置 选择能覆盖此点,且右端点最靠右的区间覆盖它 要注意特判是否有合法解,如果途中无法覆盖某点, 或者所有区间都用完了也不能覆盖完即无解 #pragma comment(linker, "/STACK:102400000,102400000")

POJ 2955 区间DP

求最多能匹配的括号数*2 #include "stdio.h"#include "string.h"int max(int a,int b){if (a<b) return b;else return a;}int judge(char a,char b){if (a=='(' && b==')') return 1;if (a=='[' && b==']') return 1

HDU 4553 线段树双关键字区间合并

用两个关键字记录,分别为屌丝时间和女神时间 若屌丝约,更新屌丝时间 若女神约,更新屌丝和女神时间 学习,则两个全部清空 #include "stdio.h"#include "string.h"struct Data{int l,r,x1,x2,l1,l2,r1,r2;}data[400010];int Max(int a,int b){if (a<b) return b