离散化思想的简单应用

离散化技术在算法中的应用

在这里插入图片描述
(^ _ ^),大家好哇,开学好久没更新了,今天我们来看看与离散化有关的题目
P3029

离散化详解

什么是离散化?

离散化是一种将大范围数值映射到紧凑区间的技术。当数据值域跨度极大(如1e9)但实际数据点稀疏(如1e5个)时,直接使用数组处理会浪费大量空间。此时可将数据映射到连续的小范围索引,从而高效处理。

为什么需要离散化?
  • 节省空间:将稀疏大范围数据压缩到小范围。
  • 适配数据结构:方便使用数组、哈希表等结构进行计数或统计。
离散化步骤
  1. 收集所有待离散化的值:将所有需要处理的数值存入临时数组。
  2. 排序:对临时数组进行排序。
  3. 去重:去除重复元素,得到唯一有序序列。
  4. 二分映射:通过二分查找将原始值映射到离散化后的索引。
示例说明

假设原始数据:[500, 3, 200, 500, 10000]

  1. 收集所有值:[500, 3, 200, 500, 10000]
  2. 排序后:[3, 200, 500, 500, 10000]
  3. 去重后:[3, 200, 500, 10000]
  4. 映射关系:3→0, 200→1, 500→2, 10000→3
模板代码解析
vector<int> alls; // 存储待离散化值
sort(alls.begin(), alls.end()); // 排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去重

// 二分查找映射值
int find(int x) {
    int l = 0, r = alls.size() - 1;
    while (l < r) {
        int mid = (l + r) >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1; // 通常映射到1-based索引
}
  • unique函数:将重复元素移至末尾,返回去重后的尾指针。
  • 二分查找:找到第一个≥x的位置,实现O(logn)查询。

题目P3029解析:牛的阵容

题目描述

题目大意:给定N头牛的位置和品种ID,选取一个最短的连续区间,使得区间内包含所有品种的牛。输出这个区间的最小长度。

输入格式

  • 第1行:整数N。
  • 第2到N+1行:每行两个整数,表示牛的位置和品种ID。

输出格式:一个整数,表示最短区间长度。

样例输入

6
25 7
26 1
15 1
22 3
20 1
30 1

样例输出

4

样例解释:选择位置22到26的牛,包含品种3、7、1,区间长度为26-22=4。


解题思路
  1. 排序处理:按牛的位置排序,方便滑动窗口处理。
  2. 离散化品种ID:将大范围的ID映射到小范围索引,便于计数。
  3. 滑动窗口(双指针)
    • 右指针扩展窗口,统计各品种出现次数。
    • 当窗口包含所有品种时,尝试左移左指针以缩小窗口,更新最小长度。

为什么用滑动窗口

  • 排序后牛的位置有序,满足滑动窗口应用的单调性。
  • 需要找到最短连续区间,滑动窗口能高效处理此类问题。

代码解析
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <climits>
using namespace std;

struct Cow {
    int position;
    int id;
};

bool compareByPosition(const Cow& a, const Cow& b) {
    return a.position < b.position; // 按位置升序排序
}

int main() {
    int n;
    cin >> n;
    vector<Cow> cows(n);
    vector<int> ids; // 存储所有品种ID用于离散化

    for (int i = 0; i < n; ++i) {
        cin >> cows[i].position >> cows[i].id;
        ids.push_back(cows[i].id);
    }

    // 按位置排序,以便滑动窗口处理
    sort(cows.begin(), cows.end(), compareByPosition);

    // 离散化品种ID
    sort(ids.begin(), ids.end());
    ids.erase(unique(ids.begin(), ids.end()), ids.end());
    int totalIds = ids.size(); // 不同品种的总数

    unordered_map<int, int> idCount; // 记录当前窗口各品种数量
    int left = 0, minCost = INT_MAX;
    int distinct = 0; // 当前窗口内的不同品种数

    // 滑动窗口遍历
    for (int right = 0; right < n; ++right) {
        int currentId = cows[right].id;
        // 离散化:找到currentId在ids中的索引
        int discreteId = lower_bound(ids.begin(), ids.end(), currentId) - ids.begin();

        if (idCount[discreteId] == 0) distinct++; // 新增品种
        idCount[discreteId]++;

        // 当窗口包含所有品种时,尝试缩小左边界
        while (distinct == totalIds && left <= right) {
            minCost = min(minCost, cows[right].position - cows[left].position);

            int leftId = cows[left].id;
            int leftDiscreteId = lower_bound(ids.begin(), ids.end(), leftId) - ids.begin();
            idCount[leftDiscreteId]--;
            if (idCount[leftDiscreteId] == 0) distinct--; // 该品种已不在窗口中
            left++;
        }
    }

    cout << minCost << endl;
    return 0;
}

关键点解释

  • 离散化处理:将品种ID映射到0~totalIds-1,便于使用数组或哈希表统计。
  • 滑动窗口维护:右指针不断扩展,当窗口包含所有品种时,左指针尽可能右移以缩小窗口,同时更新最小长度。
  • 复杂度分析:排序O(nlogn),滑动窗口遍历O(n),总复杂度O(nlogn),适用于n≤1e5的数据规模。

类似题目推荐

  1. LeetCode 56. 合并区间

    • 题目:合并所有重叠区间。
    • 离散化应用:处理区间端点,统计覆盖情况。
    • 题解链接
  2. 洛谷P1496 火烧赤壁

    • 题目:求多个区间的总覆盖长度。
    • 离散化应用:将区间端点映射到小范围,差分数组统计。
    • 题目链接
  3. LeetCode 327. 区间和的个数

    • 题目:统计区间和在某个范围内的数量。
    • 离散化应用:处理前缀和,结合树状数组高效查询。
    • 题解链接

会成为一个好程序员的!^ _ ^

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值