阵列中的反转是一对索引(i,j),使得a [i]> a [j]且i <1。学家
给定2个阵列A和B,并且我们必须返回这样的对的数量,使得a [i]> b [j]且i <1。学家
示例:
令n = 3且A [] = [5,6,7]且B [] = [1,2,3]则答案为3. 3对为(5,2),(5,3) (6,3)。
我的代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int len;
scanf("%d",&len);
int a[len];
int b[len];
for(int i = 0; i < len; i++)
scanf("%d",&a[i]);
for(int i = 0; i < len; i++)
scanf("%d",&b[i]);
int count = 0;
for (int i = 0;i < len; i++)
{
for(int j = i+1; j < len; j++)
{
if(a[i] > b[j])
{
count++;
}
}
}
printf("%d",count);
}
但这是O(N ^ 2)解决方案。我需要一个更好的解决方案,因为N <= 200000.I我知道我们可以在O(N * Log N)时间内计算相同数组中的反演。但是如何做到这一点对于两个不同的阵列?
答案 0 :(得分:6)
我written in the past关于如何使用Fenwick tree计算反转,这是一种非常有效的二叉树类型,可让您计算序列上的前缀聚合。
以下是针对您的方案的特别修改:
long long inversions(const vector<int>& a, const vector<int>& b) {
int n = a.size();
vector<int> values(a);
for (int x: b) values.push_back(x);
sort(begin(values), end(values));
vector<int> counts(2*n + 1);
long long res = 0;
for (int i = n - 1; i >= 0; --i) {
// compute sum of prefix 1..rank(a[i]) - 1
for (int v = lower_bound(begin(values), end(values), a[i]) - begin(values);
v;
v -= v & -v)
res += counts[v];
//add 1 to point rank(b[i])
for (int v = lower_bound(begin(values), end(values), b[i]) - begin(values) + 1;
v <= 2*n;
v += v & -v)
counts[v]++;
}
return res;
}
基本上我们从右到左遍历数组,维护一个数据结构,代表我们在后缀中看到的值。对于每个元素b [i],我们在最终结果中添加x&lt; = b [i] -1的数据结构中元素x的数量。然后我们将[i]添加到数据结构中。
数组values
用于将值范围压缩为1..2n,因为Fenwick树在范围大小中采用线性空间。我们可以通过选择更全面的数据结构来避免这一步骤,例如具有子树大小增强的平衡bjnary搜索树。
复杂度为O(n log n),常数因子非常低。
答案 1 :(得分:0)
您可以使用合并排序的想法找到两个数组之间的求逆!
考虑到您有两个相同大小的数组,我们称它们为A,B 如果我们分别表示B的A,A1的前半部分和A,A2以及B1和B2的后半部分,那么我们可以得出结论,答案为:
可以通过递归调用该函数来支持前两个元素,但是如何计算第三个元素?
这个想法是要从左到右穿过A1和B2, 如果B1中的元素大于A1中的元素,则应该将尚未访问的A1中的元素相加,求反,最后我们仅将A1和A2排序为A,将B1以及B2排序为B >
这是python中的代码:
def find_inv(A, B):
if len(A) <= 1:
return 0
mid = len(A) // 2
A1 = A[:mid]
A2 = A[mid:]
B1 = B[:mid]
B2 = B[mid:]
if len(A1) >= 1 and len(B2) >= 1:
ans = find_inv(A1, B1) + find_inv(A2, B2)
else:
A.sort()
B.sort()
ans = 0
len_A = len(A1)
index_A = 0
len_B = len(B2)
index_B = 0
for k in range(len_A + len_B):
if A1[index_A] <= B2[index_B]:
index_A += 1
if index_A == len_A:
merge(A1, A2, A)
merge(B1, B2, B)
return ans
else:
index_B += 1
ans += (len_A - index_A)
if index_B == len_B:
merge(A1, A2, A)
merge(B1, B2, B)
return ans
def merge(A1, A2, dest):
i = 0
j = 0
while i < len(A1) and j < len(A2):
if A1[i] < A2[j]:
dest[i+j] = A1[i]
i += 1
else:
dest[i+j] = A2[j]
j += 1
while i < len(A1):
dest[i+j] = A1[i]
i += 1
while j < len(A2):
dest[i+j] = A2[j]
j += 1
答案 2 :(得分:-1)
一个想法:
1.合并两个原始数组,使得具有相同索引的每对元素彼此相邻。 (您需要放置元素,使得较低元素位于较高元素之前)
3.计算结果数组中的反转次数,如下所述。
编辑:对不起,我误解了这个问题。如果您只想从(I)到b(j)进行反转,则可以使用另一个字段数组类型(a或b)标记每个元素。然后在mergesorting的同时,只有当反转是从arraytype a到b时,才能增加计数。