求和间隔

时间:2012-04-19 21:15:09

标签: algorithm

我必须总结这些间隔:

1..6
2..4
The result is 1..6, so there are 6 numbers in the end.

这是另一个例子:

4..6
8..10
14..16
4, 5, 6, 8, 9, 10, 14, 15, 16, the size is 9.

现在,我必须在O(N)中这样做。这是一个不太好的方法,我很快就想出了使用STL:

#include <set>
#include <stdio.h>

using namespace std;

int main() {
  int n;
  scanf("%d", &n);

  set<int> numbers;
  int a, b;
  for (int i = 0; i < n; i++) {
    scanf("%d %d", &a, &b);
    for (int u = a; u <= b; u++) {
      numbers.insert(u);
    }
  }

  printf("%d\n", numbers.size());

  return 0;
}

知道在O(N)中如何做到这一点?我知道我必须先对它进行排序,但我可以使用我刚刚制作的:

bool compare(const vector<int> first, const vector<int> second) {
  if (first[0] == second[0]) return first[1] < second[1];
  return first[0] < second[0];
}

sort(intervals.begin(), intervals.end(), compare);

所以它是O(log N + N)。

有什么想法吗?谢谢。

4 个答案:

答案 0 :(得分:2)

如果n是间隔的数量,那么我认为没有办法做到这一点,而不是O(n log(n))

但是如果我们愿意面对这一点,那么第一步是按照左手值对间隔进行排序。 (这需要时间O(n log(n))。)然后,您尝试根据以下伪代码计算联合中的最小间隔集

answer = 0
while intervals left
    (min, max) = next interval
    while intervals left and min of next interval < max:
        if max < max of next interval:
            max = max of next interval
        move forward in interval list
    # the next interval is [min..max]
    answer += max - min + 1

(此代码在间隔数量上是线性的,非线性部分正在对其进行排序。)

答案 1 :(得分:1)

我之前在OCaml做过,这是代码:

let rec calc c l1 l2 =
  match c,l1,l2 with                            
      None, (f1,t1) :: y1, ((f2,t2) :: y2 as n2) when f1 < f2 -> calc (Some (f1,t1)) y1 n2
    | None, n1, (f2,t2) :: y2 -> calc (Some (f2,t2)) n1 y2
    | None, _, _ -> []
    | (Some (fc,tc) as cur), (f1,t1) :: y1, ((f2,t2) :: y2 as n2) when t1 <= fc -> calc cur y1 n2
    | (Some (fc,tc) as cur), ((f1,t1) :: y1 as n1), (f2,t2) :: y2 when t2 <= fc -> calc cur n1 y2
    | Some (fc,tc), (f1,t1) :: y1, ((f2,t2) :: y2 as n2) when f1 <= tc && t1 > fc -> calc (Some (fc,t1)) y1 n2
    | Some (fc,tc), ((f1,t1) :: y1 as n1), (f2,t2) :: y2 when f2 <= tc && t2 > fc -> calc (Some (fc,t2)) n1 y2
    | Some (fc,tc), (f1,t1) :: y1, ((f2,t2) :: y2 as n2) when f1 < f2 -> [fc,tc] @ calc (Some (f1,t1)) y1 n2
    | Some (fc,tc), (t :: e as n1), (f2,t2) :: y2 -> [fc,tc] @ calc (Some (f2,t2)) n1 y2
    | Some (fc,tc), [], (f,t) :: tr when f <= tc && t > tc -> calc (Some (fc,t)) [] tr
    | Some (fc,tc), [], (f,t) :: tr when f <= tc && t <= tc -> calc (Some (fc,tc)) [] tr
    | Some (fc,tc), [], x -> [fc,tc] @ x
    | Some (fc,tc), (f,t) :: tr, [] when f <= tc && t > tc -> calc (Some (fc,t)) tr []
    | Some (fc,tc), (f,t) :: tr, [] when f <= tc && t <= tc -> calc (Some (fc,tc)) tr []
    | Some (fc,tc), x, [] -> [fc,tc] @ x

这计算两个范围的并集(两个元素的两个任意集合),它是O(N + M)(N和M是每个集合中单个区间的数量)。结果已排序。

在此之后,您可以轻松地以线性时间计算列表:

List.fold_left (fun a (f,t) -> for i = f to t do a := !a @ [Int i] done; a) (ref []) range

好的,这是OCaml,但我准备好了,所以它可能对你有用,特别是在通过删除重叠部分来合并间隔的棘手部分,因为我花了一些时间来弄清楚算法,但我无法描述在metacode中给你(正如你从实现中看到的那样)。

答案 2 :(得分:1)

我相信你在这里可以达到的最佳复杂度是O(N * log(N)),其中N是间隔的数量。解决方案不是很难 - 您需要首先按照开头对间隔进行排序,然后再进行另一次线性传递来计算它们的并集。我将尝试用c ++编写一些代码:

struct Interval {
  int from, to;
  bool operator<(const Interval& other) const {
    if(from != other.from) {
      return from < other.from;
    }
    return to < other.to;
  }
};

int main() {
  vector<Interval> intervals;
  sort(intervals.begin(), intervals.end());

  int current_position = intervals[0].from;
  int sum = 0;
  for (int i = 0; i < intervals.size(); ++i) {
    if (intervals[i].to < current_position) {
      continue;
    } else if (intervals[i].from <= current_position) {
      sum += intervals[i].to - current_position + 1;
      current_position = intervals[i].to + 1;
    } else {
      sum += intervals[i].to - intervals[i].from + 1;
      current_position = intervals[i].to + 1;
    }
  }
  std::cout << sum << std::endl;
  return 0;
}

答案 3 :(得分:0)

首先,让我们清楚N是什么 - 是否是细分数量?

如果是这种情况,那么你不能总是这样做 - 只需打印出所有段中的单个数字的数量 - 称为m - 需要O(m)时间。那么,最快的算法不能比O(m + n)

更好