《算法竞赛从入门到国奖》算法基础:入门篇-离散化

💡Yupureki:个人主页

✨个人专栏:《C++》 《算法》


🌸Yupureki🌸的简介:


目录

离散化

[1. 离散化思想](#1. 离散化思想)

[2. 火烧赤壁](#2. 火烧赤壁)

算法原理

实操代码

[3. 贴海报](#3. 贴海报)

算法原理

实操代码


离散化

1. 离散化思想

当题目中的数值很大,但数据量不是很大,并且我们需要利用数据的值来映射数组的下标时,我们可以采用离散化的思想,使每一个数据缩小成一个很小的值

例如对于[99999,9999999999,99999999999,9999999999999999]的数组中,元素的数值很大,我们不方便用整型或者字符串存储

这个时候我们观察数组,可以将数组进行排序 (例如从小到大),然后把元素在数组中的下标 当作一个新的值放在另一个数组中,这种叫做映射哈希表就是一个典型例子

cpp 复制代码
#include <iostream>
#include <unordered_map>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int n;
int v[N]; // 原始数据
int tmp[N]; // ⽤来排序的数组
int cnt;
unordered_map<int, int> id; // 记录离散化之后的值

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		int x; cin >> x;
		v[i] = x;
		tmp[i] = x; // 数据放进离散化数组中
	}
	// 离散化:排序 + 放进哈希表中
	sort(tmp + 1, tmp + 1 + n);
	for (int i = 1; i <= n; i++)
	{
		if (id.count(tmp[i])) continue; // 如果已经存过这个数,不做处理
		cnt++; // 这个数映射之后的值
		id[tmp[i]] = cnt; // 放进哈希表中
	}
	// 找到离散化之后的值
	for (int i = 1; i <= n; i++)
	{
		int x = v[i];
		cout << x << "离散化之后是: " << id[a[i]] << endl; // ⼆分查找离散化之后的值
	}
	return 0;
}

2. 火烧赤壁

题目链接:

P1496 火烧赤壁 - 洛谷

算法原理

抛开数据范围不看,这就是一道「差分」题目:

给定一个区间,我们可以全部执行+1操作;

最后看看整个数组中,大于0的位置有多少个。

因此可以创建一个原数组的「差分」数组,然后执行完「区间修改」操作之后,还原原数组,「统计大于0」的区间长度。

但是,这道题的「数据范围」不允许我们直接差分,因为「开不了那么大」的数组;

我们发现,区间的范围虽然很大,区间的「个数」却只有2x10^4级别。此时我们就可以:

  1. 先将所有的「区间信息」离散化
  2. 然后在「离散化的基础」上,处理所有的「区间修改」操作
  3. 处理完之后找出「原始数组对应的区间端点」,计算相应的「长度」

实操代码

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
using namespace std;

int main()
{
    int n; cin >> n;
    vector<int> l(n); vector<int> r(n);
    vector<int> tmp; unordered_map<int, int> m;
    vector<int> f(2 * n);
    for (int i = 0; i < n; i++)
    {
        int a, b; cin >> a >> b;
        l[i] = a; r[i] = b;
        tmp.push_back(a); tmp.push_back(b);
    }
    sort(tmp.begin(), tmp.end());
    for (int i = 0; i < 2 * n; i++)
    {
        m[tmp[i]] = i;
    }
    for (int i = 0; i < n; i++)
    {
        f[m[l[i]]]++;
        f[m[r[i]]]--;
    }
    for (int i = 1; i < 2 * n; i++)
    {
        f[i] += f[i - 1];
    }
    int ret = 0;
    for (int i = 0; i < 2 * n; i++)
    {
        if (f[i] <= 0)
            continue;
        int j = i;
        while (j < 2 * n && f[j] > 0)
            j++;
        ret += tmp[j] - tmp[i];
        i = j;
    }
    cout << ret;
    return 0;
}

3. 贴海报

题目链接:

P3740 [HAOI2014] 贴海报 - 洛谷

算法原理

根据题意「模拟」即可。

由于「区间的长度」很大,暴力模拟的时候会超时。但是我们发现,虽然区间长度很大,但是「区间的个数」是很少的,所以我们可以「离散化」处理一下区间的端点值,然后在「离散化的基础上」模拟覆盖情况

但是离散化在离散「区间问题」的时候一定要小心!因为我们离散化操作会把区间缩短,从而导致丢失一些点。在涉及「区间覆盖」问题上,离散化会导致「结果出错」。

比如我们这道题,如果有三个区间分别为:[2,5],[2,3],[5,6],离散化之后为:。[1,3],[1,2],[3,4]

为了避免出现上述情况,我们可以在离散化的区间[x,y]时,不仅考虑x,y这两个值,也把「

+1,y+1」也考虑进去。此时「单个区间内部」就出现空隙,「区间与区间之间」也会出现空

隙。就可以避免上述情况出现。

实操代码

cpp 复制代码
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
using namespace std;

int main()
{
    int n, m; cin >> n >> m;
    vector<int> l(m); vector<int> r(m);
    vector<int> tmp; unordered_map<int, int> s;
    vector<int> f(4 * m); vector<bool> st(n + 1, false);
    for (int i = 0; i < m; i++)
    {
        int a, b; cin >> a >> b;
        l[i] = a; r[i] = b;
        tmp.push_back(a); tmp.push_back(a + 1); tmp.push_back(b); tmp.push_back(b + 1);
    }
    sort(tmp.begin(), tmp.end());
    tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
    int sz = tmp.size();
    for (int i = 0; i < sz; i++)
        s[tmp[i]] = i;
    for (int i = 1; i <= m; i++)
    {
        int x = s[l[i - 1]]; int y = s[r[i - 1]];
        for (int j = x; j <= y; j++)
            f[j] = i;
    }
    int ret = 0;
    for (int i = 0; i < sz; i++)
    {
        int x = f[i];
        if (x == 0 || st[x])continue;
        ret++;
        st[x] = true;
    }
    cout << ret;
    return 0;
}
相关推荐
草履虫建模13 小时前
力扣算法 1768. 交替合并字符串
java·开发语言·算法·leetcode·职场和发展·idea·基础
naruto_lnq15 小时前
分布式系统安全通信
开发语言·c++·算法
Jasmine_llq16 小时前
《P3157 [CQOI2011] 动态逆序对》
算法·cdq 分治·动态问题静态化+双向偏序统计·树状数组(高效统计元素大小关系·排序算法(预处理偏序和时间戳)·前缀和(合并单个贡献为总逆序对·动态问题静态化
学嵌入式的小杨同学16 小时前
【Linux 封神之路】信号编程全解析:从信号基础到 MP3 播放器实战(含核心 API 与避坑指南)
java·linux·c语言·开发语言·vscode·vim·ux
爱吃rabbit的mq16 小时前
第09章:随机森林:集成学习的威力
算法·随机森林·集成学习
精彩极了吧16 小时前
C语言基本语法-自定义类型:结构体&联合体&枚举
c语言·开发语言·枚举·结构体·内存对齐·位段·联合
(❁´◡`❁)Jimmy(❁´◡`❁)17 小时前
Exgcd 学习笔记
笔记·学习·算法
YYuCChi17 小时前
代码随想录算法训练营第三十七天 | 52.携带研究材料(卡码网)、518.零钱兑换||、377.组合总和IV、57.爬楼梯(卡码网)
算法·动态规划
CSDN_RTKLIB17 小时前
【四个场景测试】源文件编码UTF-8 BOM
c++
不能隔夜的咖喱18 小时前
牛客网刷题(2)
java·开发语言·算法