Инверсии подмассивов
У нас есть массив A из n целых чисел, который мы планируем отсортировать. В частности, мы хотим знать, насколько близок массив к сортировке. Для этого мы вводим идею инверсии . Инверсия в массиве - это пара двух индексов i и j, таких что i < j и A [i] > A [j] . Если наш массив содержит одну инверсию, нам нужно только поменять местами A [i] и A [j] , чтобы отсортировать массив. По определению сортируется массив, содержащий 0 инверсий.
Проблема: для массива A из n целых чисел найдите сумму числа инверсий во всех подмассивах длины k . Чтобы уточнить, нужно определить количество инверсий в каждом из n - k + 1 подмассивов длины k и сложить их.
Входные данные: первая строка содержит два целых числа n и k, разделенных пробелом. Следующая строка содержит последовательность из n целых чисел, разделенных пробелом, где i- е целое число в последовательности - A [i] .
Примеры:
Ввод: arr [] = {1 2 3 4 5 6} k = 2 Выход: 0 Ввод: arr [] = {1 6 7 2} k = 3 Выход: 2 Есть два подмассива размера 3, {1, 6, 7} и {6, 7, 2}. Количество инверсии в первом подмассиве 0 и подсчет инверсий в секунду подмассив равен 2. Итак, сумма равна 0 + 2 = 2 Ввод: 8 4 12 3 14 8 15 1 4 22 Выход: 14 Ввод: 10 5 15 51 44 44 76 50 29 88 48 50 Выход: 25
[1] Наивный подход
Поначалу эта проблема кажется довольно тривиальной, и мы можем легко реализовать наивный алгоритм, чтобы ее решение было перебором. Мы просто создаем окно длиной k и прокручиваем окно вдоль A , добавляя количество инверсий в окне на каждой итерации. Чтобы найти количество инверсий, самый простой подход - использовать два цикла for для рассмотрения всех пар элементов и увеличивать счетчик, если пара образует инверсию.
This approach is very easy to implement, but is it efficient? Let’s analyze the algorithm. The outermost loop runs n – k + 1 times, once for each k-subarray of A. At each of these iterations, we find the number of inversions in the window. To do this, we consider element 1 and elements 2, …, n, then element 2 and elements 3, …, n until element n – 1 and element n. Effectively, we’re performing n + (n – 1) + (n – 2) + … + 1 = n(n + 1)/2 operations. Thus, our algorithm performs approximately (n – k + 1)(n)(n + 1)/2 operations which is O(n^3 – kn^2). Since k can range from 1 to n, the worst case performance for this algorithm is O(n^3) when k = n/2. We can do better!
C++
// C++ implementation of above approach #include <iostream> using namespace std; // Helper function, counts number of inversions // via bubble sort loop int bubble_count( int arr[], int start, int end) { int count = 0; for ( int i = start; i < end; i++) { for ( int j = i + 1; j < end; j++) { if (arr[i] > arr[j]) { count++; } } } return count; } // Inversion counting method, slides window of // [start, end] across array int inversion_count( int n, int k, int a[]) { int count = 0; for ( int start = 0; start < n - k + 1; start++) { int end = start + k; count += bubble_count(a, start, end); } return count; } // Driver Code int main() { int n = 10; int arr[n] = { 15, 51, 44, 44, 76, 50, 29, 88, 48, 50 }; int k = 5; int result = inversion_count(n, k, arr); cout << result; return 0; } // This code is contributed by PrinciRaj1992 |
Java
public class Subarray_Inversions { // Inversion counting method, slides window of [start, // end] across array static int inversion_count( int n, int k, int [] a) { int count = 0 ; for ( int start = 0 ; start < n - k + 1 ; start++) { int end = start + k; count += bubble_count(a, start, end); } return count; } // Helper function, counts number of inversions via // bubble sort loop public static int bubble_count( int [] arr, int start, int end) { int count = 0 ; for ( int i = start; i < end; i++) { for ( int j = i + 1 ; j < end; j++) { if (arr[i] > arr[j]) { count++; } } } return count; } public static void main(String[] args) { int n = 10 ; int [] arr = { 15 , 51 , 44 , 44 , 76 , 50 , 29 , 88 , 48 , 50 }; int k = 5 ; long result = inversion_count(n, k, arr); System.out.println(result); } } |
Python3
# Python3 implementation of above approach # Helper function, counts number of inversions # via bubble sort loop def bubble_count(arr, start, end): count = 0 ; for i in range (start, end): for j in range (i + 1 , end): if (arr[i] > arr[j]): count + = 1 ; return count; # Inversion counting method, slides window # of [start, end] across array def inversion_count(n, k, a): count = 0 ; for start in range ( 0 , n - k + 1 ): end = start + k; count + = bubble_count(a, start, end); return count; # Driver Code if __name__ = = "__main__" : n = 10 ; arr = [ 15 , 51 , 44 , 44 , 76 , 50 , 29 , 88 , 48 , 50 ]; k = 5 ; result = inversion_count(n, k, arr); print (result); # This code is contributed by Rajput-Ji |
C#
// C# implementation of above approach using System; public class Subarray_Inversions { // Inversion counting method, slides window of [start, // end] across array static int inversion_count( int n, int k, int [] a) { int count = 0; for ( int start = 0; start < n - k + 1; start++) { int end = start + k; count += bubble_count(a, start, end); } return count; } // Helper function, counts number of inversions via // bubble sort loop public static int bubble_count( int [] arr, int start, int end) { int count = 0; for ( int i = start; i < end; i++) { for ( int j = i + 1; j < end; j++) { if (arr[i] > arr[j]) { count++; } } } return count; } // Driver code public static void Main() { int n = 10; int [] arr = { 15, 51, 44, 44, 76, 50, 29, 88, 48, 50 }; int k = 5; long result = inversion_count(n, k, arr); Console.WriteLine(result); } } // This code is contributed by Rajput-Ji |
Выход:
25
[2] Mergesort-based Implementation
One optimization we can make is improving our inefficient, quadratic-time inversion counting method. One approach could involve using a mergesort-based method as outlined in this article. Since this runs in O(nlogn), our overall runtime is reduced to O(n^2logn), which is better, but still won’t be able to handle cases for which n = 10^6 for instance.
Java
import java.util.*; public class Subarray_Inversions { // Inversion counting method, slides window of [start, // end] across array static int inversion_count( int n, int k, int [] a) { int count = 0 ; for ( int start = 0 ; start < n - k + 1 ; start++) { int [] sub_array = new int [k]; for ( int i = start; i < start + k; i++) { sub_array[i - start] = a[i]; } count += subarray_inversion_count(sub_array); } return count; } // Counts number of inversions when merging public static long merge_inversion_count( int [] arr, int [] left, int [] right) { int i = 0 , j = 0 , count = 0 ; while (i < left.length || j < right.length) { if (i == left.length) { arr[i + j] = right[j]; j++; } else if (j == right.length) { arr[i + j] = left[i]; i++; } else if (left[i] <= right[j]) { arr[i + j] = left[i]; i++; } else { arr[i + j] = right[j]; count += left.length - i; j++; } } return count; } // Divide and conquer approach -- splits array and counts // inversions via merge method public static long subarray_inversion_count( int [] arr) { if (arr.length < 2 ) return 0 ; int m = (arr.length + 1 ) / 2 ; int left[] = Arrays.copyOfRange(arr, 0 , m); int right[] = Arrays.copyOfRange(arr, m, arr.length); return subarray_inversion_count(left) + subarray_inversion_count(right) + merge_inversion_count(arr, left, right); } public static void main(String[] args) { int n = 10 ; int [] arr = { 15 , 51 , 44 , 44 , 76 , 50 , 29 , 88 , 48 , 50 }; int k = 5 ; long result = inversion_count(n, k, arr); System.out.println(result); } } |
C#
using System; using System.Collections.Generic; public class Subarray_Inversions { // Inversion counting method, slides window of [start, // end] across array static int inversion_count( int n, int k, int [] a) { int count = 0; for ( int start = 0; start < n - k + 1; start++) { int [] sub_array = new int [k]; for ( int i = start; i < start + k; i++) { sub_array[i - start] = a[i]; } count += subarray_inversion_count(sub_array); } return count; } // Counts number of inversions when merging public static int merge_inversion_count( int [] arr, int [] left, int [] right) { int i = 0, j = 0, count = 0; while (i < left.Length || j < right.Length) { if (i == left.Length) { arr[i + j] = right[j]; j++; } else if (j == right.Length) { arr[i + j] = left[i]; i++; } else if (left[i] <= right[j]) { arr[i + j] = left[i]; i++; } else { arr[i + j] = right[j]; count += left.Length - i; j++; } } return count; } // Divide and conquer approach -- splits array and counts // inversions via merge method public static int subarray_inversion_count( int [] arr) { if (arr.Length < 2) return 0; int m = (arr.Length + 1) / 2; int []left = new int [m]; Array.Copy(arr, 0, left,0, m); int []right = new int [arr.Length - m]; Array.Copy(arr, m, right,0, arr.Length - m); return subarray_inversion_count(left) + subarray_inversion_count(right) + merge_inversion_count(arr, left, right); } public static void Main(String[] args) { int n = 10; int [] arr = { 15, 51, 44, 44, 76, 50, 29, 88, 48, 50 }; int k = 5; long result = inversion_count(n, k, arr);
РЕКОМЕНДУЕМЫЕ СТАТЬИ |