我必须总结这些间隔:
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)。
有什么想法吗?谢谢。
答案 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)
更好