OP 定义了 2 元素“不允许”数组,[i,j],j >= i。我发现引用“不被覆盖”(NTBC)范围,i..j,j >= i 更具描述性。给定一个正整数n 和一个NTBC 范围数组arr,目标是确定1..n 覆盖的范围数,这些范围没有 的NTBC 范围;也就是说,计算那些范围i..j:
arr.any? { |r| (i..j).cover? r } == false
在这里和下面,每当我提到一个范围i..j 时,都假定i 和j 满足条件1 <= i <= j <= n。
如果i < p < q < j 或p < i < q < j,我将两个NTBC 范围i..j 和p..q 称为重叠。下面我提出了一个非常快速的解决方案,适用于 no 重叠 NTBC 范围的情况。 OP 已经声明 NTBC 范围可以重叠,所以我的解决方案没有一个完整的答案。问题是重叠范围的存在破坏了我采用的组合算法所需的数学结构。尽管如此,我还是决定提出我的解决方案,希望有人能找到一种方法将我的想法扩展到 NTBC 范围可能重叠的情况。
创建count_ranges方法
让我们首先创建一个方法,给定整数i 和j,i <= j,计算p..q 的范围数,使得i <= p <= q <= j;也就是说,(i..j).cover?(p,q) #=> true。例如,
(20..40).cover?(12..18) #=> false
(20..40).cover?(14..28) #=> false
(20..40).cover?(23..37) #=> true
(20..40).cover?(29..47) #=> false
(20..40).cover?(43..51) #=> false
有关参数是范围的情况,请参阅Range#cover?。方法如下。
def count_ranges(i,j)
return 0 if j < i
n = (j-i+1)
n**2 - (n-1)*n/2
end
例如:
count_ranges(1,4) #=> 10
这些将是10 范围:
1..1, 1..2, 1..3, 1..4,
2..2, 2..3, 2..4,
3..3, 3..4,
4..4
注意:
count_ranges(3,6) #=> 10
或者,更一般地说,
count_ranges(n,n+3) #=> 10
用这种方法进行计算的原理如下。给定范围i..j、i <= j,该范围覆盖的范围数计算如下。
n = j-i+1
n + n-1 + n-2 + ... + n-(n-1)
= n*n - (1 + 2 +...+ n-1)
= n*n - (n-1)*n/2
n 等于从i 开始到k 结束的范围数,其中i <= k <= j。 n-1 等于从 i+1 开始到 k、i+1 <= k <= j 等结束的范围数。 n-(n-1) #=> 1 等于从 i+(n-1) #=> i+(j-i+1-1) => j 开始并以 k、j <= k <= j 结束的范围数。
(1 + 2 +...+ n-1)是等差数列的和,等于序列中的元素个数(n-1)与序列中的平均值的乘积,等于第一个和最后一个元素之和( 1+(n-1) #=> n) 除以2。
这个方法完成了下面代码中的所有工作。需要注意的是,这个方法的执行速度几乎与它的两个参数的大小无关。
删除多余的 NTBC 范围
让arr 成为 NTBC 范围的数组。第一步是删除覆盖另一个 NTBC 范围的 NTBC 范围。如果:
r1 = u..v
r2 = x..w
和u <= x =< w <= v (r1.cover?(r2) #=> true),r1 是多余的,因此可以忽略,因为r 的任何范围都被取消资格,因为r.cover?(r1) #=> true 将被r2 取消资格,如果@987654381 @ 不在场。
require 'set'
def remove_redundant_ranges(arr)
set = Set.new
a = arr.dup
while a.any?
r = a.shift
set << r unless set.any? { |t| r.cover?(t) } || a.any? { |t| r.cover?(t) }
end
set.to_a
end
例如:
arr = [2..4, 7..14, 8..12, 14..14, 4..5, 1..5, 9..12, 2..4]
remove_redundant_ranges(arr)
#=> [14..14, 4..5, 9..12, 2..4]
计数方法
def count_non_covering_ranges(n, arr)
(([0..0] + remove_redundant_ranges(arr).sort_by(&:begin)) << (n+1..n+1)).
each_cons(2).
sum { |r1,r2| net_count(r1, r2) }
end
def net_count(r1, r2)
r1b, r1e = endpoints(r1)
r2b, r2e = endpoints(r2)
count_ranges(r1b+1, r2e-1) - count_ranges(r1b+1, r1e-1)
end
def endpoints(r)
[r.begin, r.end]
end
示例
示例 1
n = 9
arr = [2..4, 4..5, 7..9]
count_non_covering_ranges(n, arr)
#=> 20
1..9 覆盖的20 范围不包括arr 中的任何 NTBC 范围如下。
1..1, 1..2, 1..3,
2..2, 2..3,
3..3, 3..4,
4..4,
5..5, 5..6, 5..7, 5..8,
6..6, 6..7, 6..8,
7..7, 7..8,
8..8, 8..9
9..9
示例 2
n = 12
arr = [2..4, 4..7, 9..11]
count_non_covering_ranges(n, arr)
#=> 38
1..12 覆盖的38 范围不包括arr 中的任何 NTBC 范围如下。
1..1, 1..2, 1..3,
2..2, 2..3,
3..3, 3..4, 3..5, 3..6,
4..4, 4..5, 4..6,
5..5, 5..6, 5..7, 5..8, 5..9, 5..10,
6..6, 6..7, 6..8, 6..9, 6..10,
7..7, 7..8, 7..9, 7..10,
8..8, 8..9, 8..10,
9..9, 9..10,
10..10, 10..11, 10..12,
11..11, 11..12,
12..12
示例 3
n = 12
arr = [2..4, 6..8, 9..11]
count_non_covering_ranges(n, arr)
#=> 34
1..12 覆盖的34 范围不包括arr 中的任何 NTBC 范围如下。
1..1, 1..2, 1..3,
2..2, 2..3,
3..3, 3..4, 3..5, 3..6, 3..7
4..4, 4..5, 4..6, 4..7
5..5, 5..6, 5..7,
6..6, 6..7,
7..7, 7..8, 7..9, 7..10,
8..8, 8..9, 8..10,
9..9, 9..10,
10..10, 10..11, 10..12,
11..11, 11..12,
12..12
示例 4
n = 15
arr = [2..4, 4..5, 9..12, 14..14]
count_non_covering_ranges(n, arr)
#=> 44
1..15 覆盖的44 范围不包括arr 中的任何 NTBC 范围如下。
1..1, 1..2, 1..3,
2..2, 2..3,
3..3,
4..4,
5..5, 5..6, 5..7, 5..8, 5..9, 5..10, 5..11,
6..6, 6..7, 6..8, 6..9, 6..10, 6..11,
7..7, 7..8, 7..9, 7..10, 7..11,
8..8, 8..9, 8..10, 8..11,
9..9, 9..10, 9..11,
10..10, 10..11,
11..11,
10..12, 10..13,
11..12, 11..13,
12..12, 12..13,
13..13,
15..15
示例 5
n = 15
arr = [2..4, 4..5, 9..12, 14..14]
require 'time'
n = 1_000_000_000_000
m = 1_000 # number of NTBC ranges
s = 1_000 # size of each NTBC range
q = n/m
arr = m.times.map do |i|
from = rand(i*q..(i+1)*q-1-s)
from..from+s-1
end
arr.first(3)
#=> [737321450..737322449, 1803846784..1803847783, 2536962375..2536963374]
arr.last(2)
#=> [998900666529..998900667528, 999036273747..999036274746]
t = Time.now
x = count_non_covering_ranges(n, arr)
#=> 585_410_016_606_600_423_738
puts "#{(Time.now-t).round(2)} seconds"
0.24 seconds
说明
见Enumerable#each_cons。
首先要注意的是,count_non_covering_ranges 的执行速度几乎与它的第一个参数 n 的大小和 NTBC 范围的大小无关,并且大致与其第二个参数的大小成正比。参数,arr,NTBC 范围的数量。
假设:
n = 14
arr = [2..5, 7..9, 10..13]
我们首先将其修改为:
r1 = 0..0
r2 = 2..5
r3 = 7..9
r4 = 10..13
r5 = n+1..n+1 #=> 15..15
a = [r1, r2, r3, r4, r5]
我们首先计算:
c1 = count_ranges(r1.begin+1, r2.end-1)
#=> count_ranges(1, 4) #=> 10
这是1..4 涵盖的范围数,其中没有一个涵盖任何 NTBC。这些10 范围是1..1、1..2、1..3、1..4、2..2、2..3、2..4、3..3、3..4 和@9876。
接下来我们计算:
c2 = count_ranges(r2.begin+1, r3.end-1)
#=> count_ranges(3, 8) #=> 21
c3 = count_ranges(r3.begin+1, r4.end-1)
#=> count_ranges(8, 12) #=> 15
c4 = count_ranges(r4.begin+1, r5.end-1)
#=> count_ranges(11, 14) #=> 10
现在对这些值求和:
c1 + c2 + c3 + c4
#=> 56
让我们将其与我们计算的总数进行比较:
count_non_covering_ranges(n, arr)
#=> 49
差异是由于我们重复计算了7 范围:
-
c1 和 c2 都计算范围 3..3、3..4 和 4..4;
-
c2 和 c3 都计算范围 8..8;和
-
c3 和 c4 都计算范围 11..11、11..12 和 12..12。
因此我们必须从49中减去:
count_ranges(r2.begin+1, r2.end-1) + count_ranges(r3.begin+1, r3.end-1) +
count_ranges(r4.begin+1, r4.end-1)
#=> 3 + 1 + 3 => 7
为方便起见,我还减去了以下内容:
count_ranges(r1.begin+1, r1.end-1) + count_ranges(r5.begin+1, r5.end-1)
#=> count_ranges(1, -1) + count_ranges(16, 14) => 0 + 0 => 0
这是允许的,因为我构造了count_ranges,这样count_ranges(i, j) 在j < i 时返回零。