LeetCode 2001. Number of Pairs of Interchangeable Rectangles【哈希表/数学】中等

本文探讨如何在LeetCode题目中计算给定矩形数组中,通过宽高比互换的矩形对数量。作者提供了哈希表、数学方法以及避免精度问题的解决方案,并展示了C++代码实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

You are given n rectangles represented by a 0-indexed 2D integer array rectangles, where rectangles[i] = [widthi, heighti] denotes the width and height of the ith rectangle.

Two rectangles i and j (i < j) are considered interchangeable if they have the same width-to-height ratio. More formally, two rectangles are interchangeable if widthi/heighti == widthj/heightj (using decimal division, not integer division).

Return the number of pairs of interchangeable rectangles in rectangles.

Example 1:

Input: rectangles = [[4,8],[3,6],[10,20],[15,30]]
Output: 6
Explanation: The following are the interchangeable pairs of rectangles by index (0-indexed):
- Rectangle 0 with rectangle 1: 4/8 == 3/6.
- Rectangle 0 with rectangle 2: 4/8 == 10/20.
- Rectangle 0 with rectangle 3: 4/8 == 15/30.
- Rectangle 1 with rectangle 2: 3/6 == 10/20.
- Rectangle 1 with rectangle 3: 3/6 == 15/30.
- Rectangle 2 with rectangle 3: 10/20 == 15/30.

Example 2:

Input: rectangles = [[4,5],[7,8]]
Output: 0
Explanation: There are no interchangeable pairs of rectangles.

Constraints:

  • n == rectangles.length
  • 1 <= n <= 105
  • rectangles[i].length == 2
  • 1 <= widthi, heighti <= 105

题意:用一个下标从 0 开始的二维整数数组 rectangles 来表示 n 个矩形,其中 rectangles[i] = [widthi, heighti] 表示第 i 个矩形的宽度和高度。

如果两个矩形 iji < j)的宽高比相同,则认为这两个矩形 可互换 。更规范的说法是,两个矩形满足 widthi/heighti == widthj/heightj(使用实数除法而非整数除法),则认为这两个矩形 可互换

计算并返回 rectangles 中有多少对 可互换 矩形。


解法 哈希表+数学

在竞赛中本人的写法,重写了 pair<long long, long long> 的哈希函数和判等函数,保存有理数并用哈希表计数,这样就不会有精度问题。由于使用 C n 2 C^2_n Cn2 计算可互换矩形的个数,可能整型溢出,要用 long long 来计算。

//C++ version
class Solution {
private:
    using Tuple = pair<long long, long long>;
    int gcd(int a, int b) { return !b ? a : gcd(b, a % b); }
    struct TupleHash {
        std::size_t operator()(const Tuple& k) const {
            return k.first * 1e5 + k.second;
        }
    };
    struct TupleEqual {
        bool operator()(const Tuple& lhs, const Tuple& rhs) const {
            return lhs.first == rhs.first && lhs.second == rhs.second;
        }
    };
public:
    long long interchangeableRectangles(vector<vector<int>>& rectangles) {
        unordered_map<Tuple, long long, TupleHash, TupleEqual> rec;
        for (const vector<int>& rect : rectangles) {
            int g = gcd(rect[0], rect[1]);
            ++rec[pair{rect[0] / g, rect[1] / g}];
        }
        long long ans = 0;
        for (const auto& it : rec) {
            long long num = it.second;
            ans += num * (num - 1) / 2;
        }
        return ans;
    }
};
//执行用时:416 ms, 在所有 C++ 提交中击败了100.00% 的用户
//内存消耗:141.7 MB, 在所有 C++ 提交中击败了100.00% 的用户

由于 1 <= widthi, heighti <= 105,我们可以计算 widthi / heighti ,再来计算有多少个矩形满足 widthi / heighti = widthj / heightj 。由于精度问题,难以用哈希表完成计数,不过也可以脑洞大开——先将除法结果存储为一个数组,然后从小到大排序,再进行差分,如果差分值小于 1e-8 ,就认为前后两个值相等:

//C++ version
class Solution {
public:
    double d = 1e-8;
    long long interchangeableRectangles(vector<vector<int>>& rectangles) {
        int n = rectangles.size();
        long long ans = 0, cnt = 1;
        vector<double> diff(n);
        for (int i = 0; i < n; ++i) //相除并直接保存
            diff[i] = (double)rectangles[i][0] / (double)rectangles[i][1];
        sort(diff.begin(), diff.end());
        for (int i = n - 1; i > 0; --i) diff[i] -= diff[i - 1]; //差分
        for (int i = 1; i < n; ++i) {
            if (diff[i] < d) {
                ++cnt;
                continue;
            }
            ans += (cnt * (cnt - 1)) / 2;
            cnt = 1;
        }
        ans += (cnt * (cnt - 1)) / 2;
        return ans;
    }
};
//执行用时:340 ms, 在所有 C++ 提交中击败了100.00% 的用户
//内存消耗:117.6 MB, 在所有 C++ 提交中击败了100.00% 的用户

如果不用 unordered_map ,可以使用 map 来简化代码:

//C++ version
#define mp make_pair
typedef long long LL;
typedef pair<int, int> PII;
class Solution {
public:
    long long interchangeableRectangles(vector<vector<int>>& r) {
        LL ans = 0;
        map<PII, int> rec;
        for (const vector<int>& e : r) {
            int a = e[0], b = e[1], g = __gcd(a, b);
            a /= g, b /= g;
            PII t = mp(a, b);
            ans += rec[t]; //可以和矩形e互换的矩形
            ++rec[t];
        }
        return ans;
    }
};
//执行用时:600 ms, 在所有 C++ 提交中击败了100.00% 的用户
//内存消耗:134.6 MB, 在所有 C++ 提交中击败了100.00% 的用户
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

memcpy0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值