You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

二维点集支配点计数(等级计算)的C++代码优化咨询

二维点等级统计算法优化

问题背景

需要对给定二维点集计算每个点的等级:点的等级为x、y坐标均小于等于该点的点的总数量,最终统计每个等级对应的点个数。点的最大数量为15000,现有两种实现均无法满足运行效率要求。

现有实现问题分析

暴力实现

#include <iostream>
using namespace std;

int N, x[15005], y[15005], level[15005] = {0};

int main()
{
  cin >> N;

  for (int i = 0; i < N; i++) cin >> x[i] >> y[i];

  for (int i = 0; i < N; i++) {
    int count = 0;

    for (int j = 0; j < N; j++)
      if (y[i] >= y[j] && x[i] >= x[j])
        count++;

    level[count]++;
  }

  for (int i = 1; i <= N; i++) cout << level[i] << endl;
}

该实现采用双重循环两两比对,时间复杂度为O(n²),n=15000时总运算量达2.25亿次,很容易超出时间限制。

初步排序优化版本

#include <iostream>
#include <algorithm>
#include <utility>
using namespace std;

int N, X, Y, level[15005] = {0};
pair<int, int> points[15005];

int main()
{
  cin >> N;

  for (int i = 0; i < N; i++) {
    cin >> X >> Y;
    points[i] = make_pair(X, Y);
  }

  sort(points, points + N, [](const pair<int, int> &a, const pair<int, int> &b)
       { return a.first > b.first; });

  for (int i = 0; i < N; i++) {
    int count = 0;

    for (int j = 0; j < N; j++)
      if (points[i].first >= points[j].first && points[i].second >= points[j].second)
        count++;

    level[count]++;
  }

  for (int i = 1; i <= N; i++) cout << level[i] << endl;
}

该版本虽然对点按x做了降序排序,但内层循环依然遍历全部点做双条件判断,时间复杂度仍为O(n²),排序带来的有序性完全没有被利用,因此性能没有本质提升。

优化方案

核心思路是将二维条件判断降为一维,用高效数据结构把线性统计替换为对数级统计:

  • 首先将所有点按x坐标升序排序,x坐标相同的点按y坐标升序排序。排序完成后,处理第i个点时,所有排在i前面的点x坐标必然小于等于当前点x,此时只需要统计已处理点中y坐标小于等于当前点y的数量,加上当前点自身就是该点的等级,无需再判断x维度条件。
  • 用树状数组(Fenwick Tree)维护已处理点的y坐标计数,支持O(logM) 复杂度的单点更新(插入新点的y值)和前缀和查询(统计y≤当前值的点总数),M为y坐标的取值范围。

整体时间复杂度可降到O(nlogn),n=15000时总运算量仅20万次左右,性能提升上千倍。

优化后实现代码

#include <iostream>
#include <algorithm>
#include <utility>
using namespace std;

const int MAXN = 15005;
// 若y最大值超过32000可调整该值,若y范围过大可先做离散化
const int MAXY = 32005;
int N, level[MAXN] = {0};
pair<int, int> points[MAXN];
int tree[MAXY];

int lowbit(int x) {
    return x & -x;
}

// 树状数组单点更新
void update(int idx, int val) {
    while (idx < MAXY) {
        tree[idx] += val;
        idx += lowbit(idx);
    }
}

// 树状数组前缀和查询,返回[1, idx]区间的总和
int query(int idx) {
    int res = 0;
    while (idx > 0) {
        res += tree[idx];
        idx -= lowbit(idx);
    }
    return res;
}

int main()
{
    // 关闭cin同步加速输入
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> N;

    for (int i = 0; i < N; i++) {
        cin >> points[i].first >> points[i].second;
        // 树状数组下标从1开始,若y可能取0则统一+1偏移,避免下标0导致死循环
        points[i].second++;
    }

    sort(points, points + N, [](const pair<int, int> &a, const pair<int, int> &b) {
        if (a.first == b.first) return a.second < b.second;
        return a.first < b.first;
    });

    for (int i = 0; i < N; i++) {
        int curY = points[i].second;
        int cnt = query(curY);
        // 等级计数从1开始,包含当前点自身
        level[cnt + 1]++;
        update(curY, 1);
    }

    for (int i = 1; i <= N; i++) {
        // 用'\n'代替endl,避免频繁刷新缓冲区提升输出速度
        cout << level[i] << '\n';
    }
    return 0;
}

补充说明

  • 如果题目中y坐标取值范围远大于点的数量,可以先收集所有点的y值,排序去重后做离散化映射,压缩树状数组的空间大小,进一步提升运行效率。
  • 除了树状数组,也可以用线段树实现相同的单点更新、前缀查询逻辑,但树状数组代码更短、常数更小,更适合这类场景。

内容的提问来源于stack exchange,提问作者user15867158

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 22:01:26