2024-02-03 15:58阅读: 6评论: 0推荐: 0

(坚持每天写算法)算法复习和学习part1基础算法part1-14——离散化

  直接上题目:

  离散化描写的是题目,离散化题目都是像这种,被操作的数据是”某个数据在无限长的x里面“,它的数据范围都是比较广阔的,所以被称为离散化。对于这种题目,我们要将数据以某种方式用连续的形式排到一个容器里面。(这就是映射)(ps:这里讲的是容器是为了提醒我自己)

  这道题或许有人说使用哈希表,但是求区间和哈希表的遍历是真的两端点之间全遍历一次吧,那还是没有解决问题,嗯,离散化的缺点就是存储的下标太大了,不说空间,某些操作的时间复杂度可能要爆掉。

  这一道题的思路是,其实我重新做这一道题的时候不是说没有头绪,只能说在思考我之前是怎么做的哈哈哈,我们看题目,有说到区间和,那么考虑使用前缀和,因为醋包了一盘饺子,使用前缀和的话我们需要有序数组,也就是说要排序,对哪些进行排序?对坐标,让坐标和下标的意义一样,而且题目给出的坐标有的时候是重复的,那么排序前要去重。

  存放数据的我们设置成容器vector,容器就是放东西的(主要是输入数据中输入了坐标和想要加的数值,互有联系,使用pair封装成一个数据类型(?),放到容器里面)(ps:不用数组是因为数轴坐标可能是负的,难道可以用数组下标去存坐标?),至于这些容器装什么,我会借用大佬的图。

  由于这些坐标并不是连续的数字,所以需要一个find函数来定位(查找,二分查找)她们在容器里的定位。

  除了装数据的容器,为了简便(当然不建议在装数据的容器里面放结果,会混的),我们需要一个装结果的数组。

  这个是大佬的图解:

 

   这里是代码:

复制代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 300010;//重写的时候并没有想清楚这个为什么要取n + 2m来着,然后模拟了一下过程发现,all容器除了存放x还要存放l+r,而all容器就是聚集化的核心代码
//前缀和数组要考虑到all容器
typedef pair<int , int> PII;
int a[N] , s[N];
vector<int> all;//c++放东西的就只用vector就行了,<int , int >只是一种数据类型,之所以add和query都放进去,感觉这个不用解释欸,
vector<PII> query;
vector<PII> add;

int find(int x){//查找在哪里,直接使用二分法锁数值,因为要拿到的是x在all的下标,所以r跨步会大点,也就是左边界二分法
    int l  = 0 ,  r = all.size() - 1;
    while(l < r){
        int mid = (l + r) >> 1;
        if(all[mid] >= x) r = mid ;
        else l = mid + 1;
    }
    
    return l + 1;//前缀和的数组都是要注意边界问题(衍生的差分也是同样的问题)
    
}
int main(){
    int n , m;
    cin >>n >>m;
   
    
    //分别记录存放的和查询的 , 并将它们分别放进all和add里面
    for(int i = 0 ; i < n ; i ++){
        //重写了一下,果然我忘记怎么将数据存进pair了
        int x ,c ;
        cin >> x >> c;
        add.push_back({x , c});
        all.push_back(x);
    }
    
    for(int i = 0 ; i < m ; i ++){
        int l , r;
        cin >> l >> r;
        query.push_back({l , r});
        all.push_back(l);
        all.push_back(r);
    }
    //一个重要的点,因为查询给的区间是可能重复的,但是我们为了聚集化也一起放进去了,所以要去重,总结:聚集化注意去重
    sort(all.begin() , all.end());//sort只是为了上下限
    all.erase(unique(all.begin() , all.end()) , all.end());
    
    //这个时候应该是相加了,那么应该先赋值给a数组,因为x和数组的连续的123等是不搭配的,比如x为5不代表在a数组里面下标是5
    
    for(auto item : add){
        int i = find(item.first);//item.first也就是x,find返回的是x在all的下标是多少
        a[i] += item.second;
    }
    
    for(int i = 1 ; i <= all.size() ; i ++) s[i] = s[i -1] + a[i];
        //是all.size而不用add.size的原因是,在all里面是这么排的,a是add的,b是all的,c是add的,a b c是完全有可能的,然后add又要全部加上,所以是all.size
    
    for(auto item : query){
        int l = find(item.first) , r = find(item.second);//同样的是找对应的区间,如果有些区间超过了add的范围,由于s初始化为0,所以也没问题
        cout <<s[r] - s[l - 1] << endl;
    }
    
    return 0;
}
//微微总结一下,c++放东西的就只用vector就行了,<int , int >只是一种数据类型;二分本质是查找,可以锁数值;vector的begin和end方法,以及back方法,pair的first和second等类似的属性;聚集化要注意排序查重
复制代码

  参考链接:AcWing 802. 画个图辅助理解~ - AcWing

      AcWing 802. 区间和(离散化,代码解释非常详细) - AcWing

 

本文作者:程序计算机人

本文链接:https://www.cnblogs.com/clina/p/18004853

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   程序计算机人  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 Lions Pride Audio artist
  2. 2 Salty Sailor David Arkenstone
  3. 3 Stonefire David Arkenstone
Salty Sailor - David Arkenstone
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

Not available