基础算法(7):离散化和区间合并

1.离散化

     离散化是一个很好用的技巧,可以很大程度上降低时间和空间复杂度

     离散化是把无限空间中有限的个体映射到有限的空间中去,减少空间的使用

     比如:我们有一组很大的数据 :1  3  277438  2884821   428   239823128

     如果我们想要把这些数作为数组的下标来存储的话,我们就要开辟一个很大空间的数组,但很显然,其中很多空间我们是用不到的,浪费的不是一星半点,我接受不了,题目也不会让你过。

     总的来说,离散化的特点是这样的:

1.数据值域很大,但数据个数比较少,一般来说值域<=10^5,可以用前缀和,大于这个点就要考虑离散化
2.把值域里的数映射到连续的自然数的序列中,这个映射就是把数组的值映射到对应的下标

     但也存在着一些问题,比如:

1.数组存储值域的数可能有重复的元素,需要去重
2.怎么快速去映射,我们可以通过二分来快速算出x离散化后的值,即x对应的下标

  模板   

     让我们看看是如何实现的,下面给出模板:

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 +1!= r){int mid = l + r >> 1;if (alls[mid] >= x) r = mid;else l = mid ;}return r + 1; // 映射到1, 2, ...n
}

应用

    

#include <iostream>
#include <vector>
#include <algorithm>using namespace std;
const int N = 300010; 
int n, m;
int a[N];//存储坐标插入的值
int s[N];//存储数组a的前缀和
vector<int> alls;  //存储(所有与插入和查询有关的)坐标
vector<pair<int, int>> add, query; //存储插入和询问操作的数据int find(int x) { //返回的是输入的坐标的离散化下标int l = 0, r = alls.size() - 1;while (l+1!=r) {int mid = l + r >> 1;if (alls[mid] >= x) r = mid;else l = mid ;}return r + 1;
}int main() {scanf("%d%d", &n, &m);for (int i = 1; i <= n; i++) {int x, c;scanf("%d%d", &x, &c);add.push_back({x, c});alls.push_back(x);}for (int i = 1; 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());//执行前n次插入操作for (auto item : add) {int x = find(item.first);a[x] += item.second;}//前缀和for (int i = 1; i <= alls.size(); i++) s[i] = s[i-1] + a[i];//处理后m次询问操作for (auto item : query) {int l = find(item.first);int r = find(item.second);printf("%d\n", s[r] - s[l-1]);}return 0;
}

     这道题数据的值域已经达到了10^9,远远比10^5大,所以前缀和是用不了的,我们要使用离散化。我们首先将所有操作相关的数据分别存入到add和query容器中,把与操作有关的坐标存入alls容器中;接下来就是要对alls容器的数据进行排序去重的操作,这里用到了unique函数,这个函数把一个序列的重复数据从小到大放在序列末尾,并返回重复数据的第一个数据的迭代器。接下来就可以把每个坐标映射为一个自然数序列,并把它们存储的值插入到a数组中了,我们先对输入坐标进行离散化,即find函数执行的操作,这样再插入,每个值对应的下标就是一个自然数序列了,之后我们再对a数组求前缀和,执行询问操作就很简单了,这里借用两张图:

2.区间合并

     区间合并也是一种算法,主要用来合并区间,没错,就是这么简单粗暴,下面先给出模板,结合例题来领悟这种算法。

模板:

vector<PII> merge(vector<PII>& segs)
{vector<PII>res;sort(segs.begin(),segs.end());//按左端点进行排序int st=-2e9,ed=-2e9;//st表示区间左端点,ed表示区间右端点for(auto seg:segs)if(ed<seg.first)//情况1:两个区间无法合并,该区间的右端点比另一个区间的左端点还要小{if(st!=-2e9)res.push_back({st,ed});//区间1放进res数组st=seg.first,ed=seg.second;//维护区间2}else ed=max(ed,seg.second);//两个区间可以合并,且区间1不包含区间2,区间2不包含区间1//如果是区间1包含区间2,这时候不需要任何操作if(st!=-2e9)res.push_back({st,ed});//循环结束之后,st,ed变量不需要继续维护,因为这是最后一个序列,不能继续进行合并,直接放进res数组中即可return res;

       这就是区间合并的模板,下面看例题:

应用:

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;typedef pair<int,int>PII;const int N=100010;int n;
vector<PII>segs;vector<PII> merge(vector<PII>& segs)
{vector<PII>res;sort(segs.begin(),segs.end());//按左端点进行排序int st=-2e9,ed=-2e9;//st表示区间左端点,ed表示区间右端点for(auto seg:segs)if(ed<seg.first)//情况1:两个区间无法合并,该区间的右端点比另一个区间的左端点还要小{if(st!=-2e9)res.push_back({st,ed});//区间1放进res数组st=seg.first,ed=seg.second;//维护区间2}else ed=max(ed,seg.second);//两个区间可以合并,且区间1不包含区间2,区间2不包含区间1//如果是区间1包含区间2,这时候不需要任何操作if(st!=-2e9)res.push_back({st,ed});//循环结束之后,st,ed变量不需要继续维护,因为这是最后一个序列,不能继续进行合并,直接放进res数组中即可return res;
}
int main()
{cin>>n;for(int i=0;i<n;i++){int l,r;cin>>l>>r;segs.push_back({l,r});}vector<PII>res=merge(segs);cout<<res.size()<<endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/313585.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI时代系列丛书(由北京大学出版社出版)

前言 在AI时代&#xff0c;程序员面临着新的机遇和挑战。为了适应这个快速发展的时代&#xff0c;掌握新技能并采取相应的应对策略是至关重要的。 对于办公人员或程序员来说&#xff0c;利用AI可以提高工作效率。例如&#xff0c;使用AI助手可以帮助自动化日常的重复性工作&a…

Feign远程调用丢失请求头问题处理

在浏览器发送一个Q请求,请求中原包含请求头headers信息,controller某个A方法接收到Q请求并调用某个B方法。B方法中有Feign远程调用,在执行Feign远程调用其他服务时,会丢失掉原来请求中的请求头信息。 Feign远程调用底层是发送Http请求,而发送请求时会经过Feign的拦截器。…

使用Pycharm给html文件添加浏览器

1、选择菜单栏的File---->选择setting设置 2、选择Tools(工具)---> Web Browser(web 浏览器) 勾选 自己想要添加的浏览器前面 的勾选框即可 注意点击ok进行保存

Python数据科学应用从入门到精通--Python读取、合并SPSS数据文件

在很多情况下&#xff0c;我们需要调用SPSS软件产生的数据&#xff0c;下面通过示例来进行讲解。首先需要将本书提供的数据文件存储在安装spyder-py3的默认路径位置&#xff08;C:/Users/Administrator/.spyder-py3/&#xff0c;注意具体的安装路径可能与此不同&#xff09;&am…

【MATLAB】【数字信号处理】线性卷积和抽样定理

已知有限长序列&#xff1a;xk1,2,1,1,0,-3, hk[1,-1,1] , 计算离散卷积和ykxk*h(k) 。 程序如下&#xff1a; function [t,x] My_conv(x1,x2,t1,t2,dt) %文件名与函数名对应 %自写的卷积函数 x conv(x1,x2)*dt; t0 t1(1) t2(1); L length(x1) length(x2)-2; t t0:dt…

吉良吉影狂喜!HandRefiner:一种可以有效修正畸形手部图像的技术

这种方法首先使用深度学习模型从图片中识别出手部区域。然后它将手部 crops 出来&#xff0c;并利用一个生成对抗网络试图生成一个更加符合人体工学标准的手部形状。 GitHub&#xff1a;https://github.com/wenquanlu/HandRefiner/ 论文&#xff1a;https://arxiv.org/abs/231…

交通 | 司乘匹配:基于增量成本计算的优化算法

编者按&#xff1a; 司乘匹配是打车服务中一项至关重要的任务&#xff0c;如果这一步做得不够优化&#xff0c;可能导致乘客需要更长的时间才能到达目的地&#xff0c;同时司机的收入也会因此减少。由于司乘匹配是一个持续进行的过程&#xff0c;每一时刻都在不断涌入新的打车…

Android开发中使用Coil

Coil - Android开源图像加载库 Coil是一个开源的图像加载库&#xff0c;用于在Android中显示网络或本地图像资源。 为什么我们使用Coil&#xff1f; 快速&#xff1a;Coil进行了许多优化&#xff0c;包括内存和磁盘缓存、内存中的图像降采样、自动暂停/取消请求等。轻量级&a…

C++每日一练(10):线性查找

题目描述 输入n个数和一个需要查找的目标数&#xff0c;进行线性查找。 输入 第一行输入n&#xff08;1<n<1000&#xff09;&#xff0c; 第二行输入n个整数&#xff0c; 第三行输入要查找的目标数t。 输出 输出查找到的目标数的排序号&#xff0c;若查不到则输出no。 输…

Linux 安装Jupyter notebook 并开启远程访问

文章目录 安装Python安装pip安装Jupyter启动Jupyter Notebook1. 生成配置文件2. 创建密码3. 修改jupyter notebook的配置文件4. 启动jupyter notebook5. 远程访问jupyter notebook 安装Python 确保你的系统上已经安装了Python。大多数Linux发行版都预装了Python。你可以在终端…

基于Java SSM框架实现房屋租赁合同系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现房屋租赁合同系统演示 摘要 在网络高速发展的时代&#xff0c;众多的软件被开发出来&#xff0c;给用户带来了很大的选择余地&#xff0c;而且人们越来越追求更个性的需求。在这种时代背景下&#xff0c;人们对房屋租赁系统越来越重视&#xff0c;更好的…

【2023年终总结】 | 时光之舟:乘载着回忆与希望穿越2023,抵达2024

文章目录 1 回忆2 希望 1 回忆 2023年对我来说是非常梦幻的一年&#xff0c;我在2023年初的时候确认去做AI方向&#xff0c;在这之前我尝试了前端开发&#xff0c;移动App开发&#xff0c;云FPGA等方向&#xff0c;但是感觉自己都不是很喜欢&#xff0c;然后就开始尝试新的方向…