Мне нужен лучший алгоритм, чтобы решить эту проблему

Вот вопрос (ссылка: http://opc.iarcs.org.in/index.php/problems/FINDPERM):

Перестановка чисел 1, …, N является перегруппировкой этих чисел. Например
2 4 5 1 7 6 3 8
это перестановка 1,2, …, 8. Конечно,
1 2 3 4 5 6 7 8
также перестановка 1, 2, …, 8.
С каждой перестановкой N связана особая последовательность натуральных чисел длины N, называемая ее инверсионной последовательностью. I-й элемент этой последовательности — это число чисел j, которые строго меньше i и отображаются справа от i в этой перестановке. Для перестановки
2 4 5 1 7 6 3 8
последовательность инверсии
0 1 0 2 2 1 2 0
2-й элемент равен 1, потому что 1 строго меньше 2, и он появляется справа от 2 в этой перестановке. Точно так же 5-й элемент равен 2, поскольку 1 и 3 строго меньше 5, но отображаются справа от 5 в этой перестановке и так далее.
В качестве другого примера, последовательность инверсии перестановки
8 7 6 5 4 3 2 1
является
0 1 2 3 4 5 6 7
В этой задаче вам будет дана последовательность инверсии некоторой перестановки. Ваша задача — восстановить перестановку из этой последовательности.

Я придумал этот код:

#include <iostream>

using namespace std;

void insert(int key, int *array, int value , int size){
int i = 0;
for(i = 0; i < key; i++){
int j = size - i;
array[ j ] = array[ j - 1 ];
}
array[ size - i ] = value;
}

int main(){

int n;
cin >> n;
int array[ n ];
int key;

for( int i = 0; i < n; i++ ){
cin >> key;
insert( key, array, i + 1, i);
}

for(int i = 0;i < n;i ++){
cout << array[i] << " ";
}

return 0;
}

Он работает нормально и дает правильный ответ для 70% тестовых случаев, но пересекает ограничение по времени для остальных.
Есть ли другой, более быстрый алгоритм для решения этого вопроса?

8

Решение

Ваш алгоритм имеет сложность O(N^2) операции так для массивов размера 10^5 это требует слишком много времени для выполнения. Я пытаюсь описать лучшее решение:

У нас есть N номера. Позволяет вызвать обратный массив I, Решая эту проблему, мы должны знать, где находится K-th позиция от конца перестановки, которая все еще свободна (давайте вызовем эту функцию F(K)). Сначала мы ставим номер N позиционировать F(I[N] + 1), а затем поставить номер N-1 позиционировать F(I[N-1] + 1) и так далее.

F можно рассчитать следующим образом: объявить массив M размера N: 1 1 1 ... 1определить S(X) = M[1] + M[2] + ... + M[X], S известен как сумма префикса. F(K) равно N плюс 1 минус такой ниже X тот S(X) = K, Каждый раз, когда мы размещаем номер Z позиционировать N + 1 - X(for K = I[Z] + 1) мы ставим ноль в M[X], Найти X быстрее, чем в O(N) время, которое я могу предложить использовать Двоичные индексированные деревья рассчитать суммы префиксов в O(logN) время и Бинарный поиск найти такой X тот S(X) равно некоторому предопределенному значению.

Общая сложность такого алгоритма O(N(log(N))^2), Это
реализация в Ruby (вы можете поиграть с ним прямо в ideone: изменить ввод, запустить и проверить вывод):

# O(n(log(n))^2) solution for http://opc.iarcs.org.in/index.php/problems/FINDPERM

# Binary Indexed Tree (by Peter Fenwick)
# http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=binaryIndexedTrees
class FenwickTree

# Initialize array 1..n with 0s
def initialize(n)
@n = n
@m = [0] * (n + 1)
end

# Add value v to cell i
def add(i, v)
while i <= @n
@m[i] += v
i += i & -i
end
end

# Get sum on 1..i
def sum(i)
s = 0
while i > 0
s += @m[i]
i -= i & -i
end
s
end

# Array size
def n
return @n
end

end

# Classical binary search
# http://en.wikipedia.org/wiki/Binary_search_algorithm
class BinarySearch

# Find lower index i such that ft.sum(i) == s
def self.lower_bound(ft, s)
l, r = 1, ft.n
while l < r
c = (l + r) / 2
if ft.sum(c) < s
l = c + 1
else
r = c
end
end
l
end

end

# Read input data
n = gets.to_i
q = gets.split.map &:to_i

# Initialize Fenwick tree
ft = FenwickTree.new(n)
1.upto(n) do |i|
ft.add i, 1
end

# Find the answer
ans = [0] * n
(n - 1).downto(0) do |i|
k = BinarySearch.lower_bound(ft, q[i] + 1)
ans[n - k] = i + 1
ft.add k, -1
end
puts ans.join(' ')

Решение с O(N(log(N))) время тоже существует. Он использует какой-то Двоичное дерево поиска: создаем BST с номерами 1, 2, 3, ... N на вертикали, тогда мы можем найти K-th число по значению в O(log(N)) и удалить вершины в O(log(N)) время тоже.

Также решение с станд :: набор может существует, но я не могу думать сейчас.

PS. Я также могу предложить вам прочитать некоторые книги по альго и олимпиадам, такие как Skienna (Проблемы программирования) или Cormen (Введение в алгоритмы).

PPS. Извините за неправильное решение, которое я описал ранее

7

Другие решения

Самая дорогая часть — это, очевидно, смещение до 100 000 элементов в вашем массиве результатов.

Если вы разделите этот массив на несколько кусков, вы можете ускорить его с помощью некоторого значительного фактора.

Если вы сказали «10 блоков» и запомнили количество элементов для каждого блока, вы выбираете правильный блок для записи в соответствии с ключом, а затем должны сдвигать элементы только для этого блока (до 10 раз меньше).

Новая проблема заключается в том, как добиться хорошего распределения чисел по кускам.


Также вы можете использовать связанный список: http://www.cplusplus.com/reference/stl/list/

Это очень эффективно при вставках, но отстой для случайного поиска. Но поиск — это просто операция чтения, поэтому поиск элементов x может быть быстрее (IDK), чем смещение элементов x в массиве.

Тогда вы можете использовать комбинированный подход и связать список с несколькими указателями, чтобы вы всегда могли искать из ближайшего.

3

Вот действительно хороший алгоритм наряду с необходимым кодированием на C ++:

Проблема решается с помощью того факта, что если на месте 7 есть 2, то два пустых поля
остаются до размещения 7. Итак, если 0 на 8, а 2 на 7, то обратный массив результатов выглядит
как: 8 _ _ 7 _ _ _ _.

Теперь разложение квадратного корня завершено, и вставка завершена:

#include <iostream>
#include <math.h>
using namespace std;

int main()
{
int n, k = 0, d, r, s, sum, temp, m, diff, check = 1;
cin >> n;

d = sqrt(n) + 1;
int arr[n], result[n], indices[d], arr2[d][d], num = 1;

for (int i = 0; i < n; i++)
cin >> arr[i];               //The inversion sequence is accepted.

for (int i = 0; i < d; i++)
indices[i] = 0;              //Indices tell where to start counting from in each row.

for (r = 0; r < d; r++)
{
for (s = 0; s < d; s++)
{
arr2[r][s] = num;       //Array is filled with 1 to n (after sqrt decomposition).
num = num + 1;
if (num == n+1)
{
check = 0; break;
}
}
if (check == 0)
break;
}

int l = s;
while (l >= 0)                  //Non-Zero numbers are shifted to right and 0 placed in
{                               //empty spaces.
arr2[r][d-1 - k] = arr2[r][l];
k = k + 1; l = l - 1;
}

k = d-1 - k + 1;
for (int t = 0; t < k; t++)
arr2[r][t] = 0;

indices[r] = indices[r] + k;    //Index of the last row is shifted to first non-zero no.

for (int i = n-1; i >= 0; i--)
{
sum = 0; m = 0;
while (sum < arr[i] + 1)
{
sum = sum + (d - indices[m]); //Empty boxes in each row are counted.
m = m + 1;
}

m = m - 1;
sum = sum - (d - indices[m]);     //When sum = 1 + corresponding value in inversion
diff = arr[i] + 1 - sum;          //sequence, then that particular value is made 0
temp = indices[m] + diff - 1;     //and (that value - 1) is the index of the number
//to be placed in result array.
result[arr2[m][temp] - 1] = i+1;
for (int w = temp - 1; w >= indices[m]; w--)
{
arr2[m][w + 1] = arr2[m][w];  //Then, 0 is shifted to just before non-zero number
}                                 //in the row, and the elements are shifted right
arr2[m][indices[m]] = 0;          //to complete the sort.
indices[m] = indices[m] + 1;
}                                     //This is done n times for 1 to n integers thus
//giving the permutation in reverse order in result
for (int p = n-1; p >= 0; p--)        //array.
cout << result[p] << ' ';

return 0;
}
2

Ваш алгоритм неэффективен для этой проблемы, потому что ваша сложность O (n ^ 2), что означает 10 ^ 10 операций для некоторых входных случаев. Вы должны найти решение, которое дешевле.

Я предлагаю вам следующий алгоритм (индексы от 1 до N):

for i=1 to N
input a(i)
if i==1 insert 1 into b
else insert i into b at place i-a(i)
end
1
По вопросам рекламы [email protected]