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

💡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;
}
相关推荐
散峰而望2 小时前
OJ 题目的做题模式和相关报错情况
java·c语言·数据结构·c++·vscode·算法·visual studio code
疋瓞2 小时前
C/C++查缺补漏《5》_智能指针、C和C++中的数组、指针、函数对比、C和C++中内存分配概览
java·c语言·c++
闻林禹2 小时前
c++并发编程
开发语言·c++
CTO Plus技术服务中2 小时前
一栈式、系统性的C、C++、Go、网络安全、Linux运维开发笔记和面试笔记
c++·web安全·golang
zc.ovo2 小时前
线段树优化建图
数据结构·c++·算法·图论
WaWaJie_Ngen2 小时前
C++实现一笔画游戏
c++·算法·游戏·游戏程序·课程设计
程序员-King.2 小时前
day140—前后指针—删除排序链表中的重复元素Ⅱ(LeetCode-82)
数据结构·算法·leetcode·链表
小尧嵌入式2 小时前
【Linux开发一】类间相互使用|继承类和构造写法|虚函数实现多态|五子棋游戏|整数相除混合小数|括号使用|最长问题
开发语言·c++·算法·游戏
黎雁·泠崖2 小时前
Java数组进阶:内存图解+二维数组全解析(底层原理+Java&C差异对比)
java·c语言·开发语言