点和细分

时间:2015-11-17 08:55:19

标签: algorithm sorting search

我正在做在线课程并且遇到了这个问题。

第一行包含两个非负整数1≤n,m≤50000 - 一行中的段数和点数。接下来的n行包含两个整数a_i≤b_i,用于定义第i个段。下一行包含定义点的m个整数。所有整数的绝对值最多为10 ^ 8。对于每个段,从n - 点表中输出它使用的点数。

我的解决方案是:

for point in points:
    occurrence = 0
    for l, r in segments:
        if l <= point <= r:
           occurrence += 1
    print(occurrence),

该算法的复杂性为O(m * n),显然效率不高。解决这个问题的最佳方法是什么?任何帮助将不胜感激!

示例输入:

2 3
0 5
7 10
1 6 11

示例输出:

1 0 0

示例输入2:

1 3
-10 10
-100 100 0

示例输出2:

0 0 1

4 个答案:

答案 0 :(得分:10)

您可以使用sweep line algorithm来解决此问题。

首先,将每个细分为两个点,即开放点和关闭点。

将所有这些点与m点相加,并根据其位置排序

迭代点列表,保持counter,每次遇到开放点时,增加counter,如果遇到终点,请减少它。如果您在列表m点中遇到一个点,则此点的结果是此时counter的值。

For example 2, we have:

1 3
-10 10
-100 100 0

After sorting, what we have is:

-100 -10 0 10 100

At point -100, we have `counter = 0`

At point -10, this is open point, we increase `counter = 1`

At point 0, so result is 1

At point 10, this is close point, we decrease `counter = 0`

At point 100, result is 0

So, result for point -100 is 0, point 100 is 0 and point 0 is 1 as expected.

时间复杂度 O((n + m)log(n + m))

答案 1 :(得分:1)

[原始答案]每个点使用的段数

我不确定我是否正确地解决了问题,但看起来像直方图使用的简单例子......

  1. 创建计数器数组(每个点一项)
  2. 将其设为零
  3. 处理递增每个使用过的点计数器的最后一行O(m)

  4. 阅读直方图O(n)

  5. 撰写答案

    所以结果应该是O(m+n)类似于(C ++):

    const int n=2,m=3;
    const int p[n][2]={ {0,5},{7,10} };
    const int s[m]={1,6,11};
    int i,cnt[n];
    for (i=0;i<n;i++) cnt[i]=0;
    for (i=0;i<m;i++) if ((s[i]>=0)&&(s[i]<n)) cnt[s[i]]++;
    for (i=0;i<n;i++) cout << cnt[i] << " "; // result: 0 1
    

    但是你可以看到p[]坐标永远不会被使用,所以我在你的问题描述中遗漏了一些东西,或者你遗漏了某些东西,或者只是为了欺骗解决者...

    清除OP中的不一致后,

    [edit1]结果略有不同

    每个细分使用的点数:

    1. 创建计数器数组(每个细分一个项目)
    2. 将其设为零
    3. 处理递增每个使用过的点计数器的最后一行O(m)
    4. 通过阅读直方图O(m)
    5. 来撰写答案

      所以结果是O(m)类似于(C ++):

      const int n=2,m=3;
      const int p[n][2]={ {0,5},{7,10} };
      const int s[m]={1,6,11};
      int i,cnt[m];
      for (i=0;i<m;i++) cnt[i]=0;
      for (i=0;i<m;i++) if ((s[i]>=0)&&(s[i]<n)) cnt[i]++;
      for (i=0;i<m;i++) cout << cnt[i] << " "; // result: 1,0,0
      

      <强> [注释]

      将新的样本集添加到OP之后,现在很清楚:

      • 索引从0
      • 开始
      • 问题是表p[n]中有多少点真正被每个段使用(输出中m个数字)

答案 2 :(得分:1)

使用二进制搜索。

根据第一个值第二个值对线段进行排序。如果您使用c++,则可以使用以下自定义排序:

sort(a,a+n,fun);    //a is your array of pair<int,int>, coordinates representing line

bool fun(pair<int,int> a, pair<int,int> b){
    if(a.first<b.first)
        return true;
    if(a.first>b.first)
        return false;
    return a.second < b.second;
}

然后,对于每个点,找到捕获该点的第一行和不捕获该点的第一行(当然在该行之后)。如果没有线捕获该点,则可以返回-1或者其他东西(而不是检查没有的点)。

类似的东西:

int checkFirstHold(pair<int,int> a[], int p,int min, int max){     //p is the point

    while(min < max){
        int mid = (min + max)/2;
        if(a[mid].first <= p && a[mid].second>=p && a[mid-1].first<p && a[mid-1].second<p)   //ie, p is in line a[mid] and not in line a[mid-1]
            return mid;
        if(a[mid].first <= p && a[mid].second>=p && a[mid-1].first<=p && a[mid-1].second>=p)   //ie, p is both in line a[mid] and not in line a[mid-1]
            max = mid-1;
        if(a[mid].first < p && a[mid].second<p )   //ie, p is not in line a[mid]
            min = mid + 1;
    }
return -1; //implying no point holds the line
}

同样,编写一个checkLastHold函数。

然后,为每个点找到checkLastHold - checkFirstHold,这就是答案。

此解决方案的复杂性为O(n log m),因为每次计算需要(log m)。

答案 3 :(得分:0)

这是我在Java中基于计数器的解决方案。 请注意,所有点,段开始和段结束都被读入一个数组。

如果不同PointType的点具有相同的x坐标,则在段开始之后和段结束之前对该点进行排序。如果它与段开始(计数器已经增加)和段结束(计数器尚未减少)一致,则将该点计数为“在”段中。

为了以与输入点相同的顺序存储答案,我创建大小为result的数组pointsCount(仅计算点数,而不是段)并使用索引{设置其元素{1}},用于存储原始输入中点的位置。

SuperPoint.index