我尝试通过Codility解决问题,
我们在飞机上绘制了N张光盘。光盘的编号从0到N −1。给出了由N个非负整数组成的数组A,用于指定光盘的半径。绘制第J个圆盘时,其中心为(J,0),半径为A [J]。
我们说如果J≠K并且第J和第K光盘具有至少一个公共点(假设这些光盘包含边界),则第J光盘和第K光盘相交。
下图显示了N = 6和A绘制的光盘,如下所示:
A[0] = 1
A[1] = 5
A[2] = 2
A[3] = 1
A[4] = 4
A[5] = 0
有十一对(无序)的光盘相交,即:
光盘1和4相交,并且都与所有其他光盘相交。 光盘2也与光盘0和3相交。 编写函数:
class Solution { public int solution(int[] A); }
,给定如上所述的描述N个光盘的数组A,它返回(无序)对相交光盘的数量。如果相交对的数量超过10,000,000,则该函数应返回-1。
给出上面显示的数组A,该函数应返回11,如上所述。
假设:
N是[0..100,000]范围内的整数; 数组A的每个元素都是[0..2,147,483,647]范围内的整数。 复杂度:
预期的最坏情况下的时间复杂度为O(N * log(N)); 预期的最坏情况下的空间复杂度为O(N)(不计算输入参数所需的存储空间)。
我也有一个我试图理解的解决方案,
public static int solution(int[] A) {
int N = A.length;
int[] sum = new int[N];
for (int i = 0; i < N; i++) {
int right;
if (N - 1 >= A[i] + i) {
right = i + A[i];
} else {
right = N - 1;
}
sum[right]++;
}
for (int i = 1; i < N; i++) {
sum[i] += sum[i - 1];
}
int result = N * (N - 1) / 2;
for (int j = 0; j < N; j++) {
int left;
if (j - A[j] < 0) {
left = 0;
} else {
left = j - A[j];
}
if (left > 0) {
result -= sum[left - 1];//.
}
}
if (result > 10000000) {
return -1;
}
return result;
}
虽然我部分地了解了解决方案,但我无法完全理解它。我在下面描述,
i。我们创建了一个长度为sum
的数组N
,并用光盘的最右边的点作为索引填充。如果最右边的点大于N-1
,我们将其设置为N-1
ii。执行数组sum
的前缀和,即扫描数字x0,x1,x2,...的序列是第二个数字y0,y1,y2,...,前缀和的序列(输入序列的运行总计):
x0 = x0
x1 = x0 + x1
x2 = x0 + x1+ x2
iii。计算提供的数组N * (N - 1) / 2
结果来自从2个元素的15种可能组合中进行选择,即C(N,R)
。如果R = 2
,我们可以推导C(N,R)= N!/ R!*(NR)!到N * (N - 1) / 2
iv。找到最左边的点,如果该值小于零,则将其设置为零。然后,如果左值大于零,则将其转换为索引和结果中的内容。
最重要的是,我无法理解最后一步。谁能解释得更好?我认为在result -= sum[left - 1]
的最后一行中,我们从最大可能值中减去对之间没有交集,现在我尝试理解计算结果
答案 0 :(得分:2)
The idea behind the solution is complement, it's easy to compute the total of the intersection and it's also not hard to get the total amount of un-intersected pairs.
And then we have the solution you provided, there are four steps in the solution:
N-1
as the rightmost) that the circles can reach for each circle and that's what we do in the first loop
;loop
;left = j - A[j];
(can be adjacent but not intersected, the left
here is the point the current circle can reach leftmost). // count the rightmost point for each circle;
for (int i = 0; i < N; i++) {
int right;
if (N - 1 >= A[i] + i) {
right = i + A[i];
} else {
right = N - 1;
}
sum[right]++;
}
// summing up since `i` cannot be reached/intersected from the left, there is no way the `right-er` can;
for (int i = 1; i < N; i++) {
sum[i] += sum[i - 1];
}
// get the total amount of combinations;
int result = N * (N - 1) / 2;
for (int j = 0; j < N; j++) {
int left; // the leftmost point the current circle can reach;
if (j - A[j] < 0) { // avoid invalid;
left = 0;
} else {
left = j - A[j];
}
if (left > 0) { // if it's valid, sum[left-1] will be the un-intersected, subtract it;
result -= sum[left - 1];
}
}
答案 1 :(得分:0)
我提供了以下解决方案的说明
在前缀和之后,sum[i]
存储0 to i (inclusive)
中最右边的光盘数量。如果为i = N-1
,则它将存储0 to i or higher (inclusive)
在最后一个循环中,left是光盘最左边的点的值,sum [left -1]是0 to (left-1)
内最右边的光盘的计数。因此,对于该特定的光盘,不可能与这些光盘相交,因此我们需要从最大可能的交点中减去计数。
我们的目的是为特定的光盘查找不相交的光盘数量。对于具有第j个索引的特定光盘,最左边的点将是 就前缀数组而言,如果 如果最左边的值小于0,则将其设置为0,如果光盘的最右边的点大于j - A[j]
,对于所有中心为i(可变)的光盘,如果i + A [i] j
是光盘的最左点,则它不会与总共sum[j-1]
个光盘相交。 N-1
,则将其设置为N-1
。因为,如果最左边的点小于0,并且可以相交,则同一张光盘的最右边的点将位于=> 0(如果是点,则为=)。因此,在比较光盘是否难处理时,将考虑使用该光盘。当我们将最右边的点> N-1