Можем ли мы вычислить это менее чем за O (n*n) ...(nlogn или n)

это вопрос, заданный мне очень очень известным МНК. Вопрос заключается в следующем ...

введите 2D n * n массив 0-х и 1-х. Если A (i,j) = 1, то все значения, соответствующие I-й строке и J-му столбцу, будут равны 1. Если есть 1 уже остается 1.

в качестве примера, если у нас есть массив

  1 0 0 0 0 
  0 1 1 0 0 
  0 0 0 0 0 
  1 0 0 1 0
  0 0 0 0 0

мы должны получить результат как

 1 1 1 1 1
 1 1 1 1 1
 1 1 1 1 0
 1 1 1 1 1
 1 1 1 1 0

входная матрица разрежена заселенный.

возможно ли это менее чем за O(N^2)?

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

P. S: мне не нужны ответы, которые дают мне сложность O(N*N). Это не домашнее задание. Я много пробовал и не мог получить правильное решение и думал, что могу получить некоторые идеи здесь.Оставьте печать в стороне для сложности

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

13 ответов


в худшем случае вам может потребоваться переключить n * n - n бит от 0 до 1 для генерации вывода. Казалось бы, вы довольно хорошо застряли с O(N*N).


Я бы предположил, что вы можете оптимизировать его для лучшего случая, но я склонен сказать, что ваш худший случай все еще O(N*N): ваш худший случай будет массивом всех 0, и вам придется исследовать каждый отдельный элемент.

оптимизация будет включать пропуск строки или столбца, как только вы нашли " 1 " (я могу предоставить подробную информацию, но вы сказали, что вас не волнует O( N*N)", но если у вас нет метаданных, указывающих, что вся строка / столбец пуста, или если у вас нет SIMD-стиль способ проверить несколько полей одновременно (скажем, если каждая строка выровнена на 4, и вы можете прочитать 32 бит данных, или если ваши данные в виде битовой маски), вам всегда придется иметь дело с проблемой массива все-ноль.


ясно, что ни выходная матрица, ни ее отрицательная версия не должны быть разреженными (возьмите матрицу с половиной первой строки, установленной на 1, и что-нибудь еще на 0, чтобы увидеть), поэтому время зависит от того, какой формат вы можете использовать для вывода. (Я предполагаю, что вход-это список элементов или что-то эквивалентное, поскольку в противном случае вы не могли бы воспользоваться разреженностью матрицы.)

простое решение для o (M+N) пространства и времени (M-количество единиц во входной матрице): возьмите два массивы длины N, заполненные единицами, перебирают все единицы на входе, и для каждого падения координата X из первого массива и Y из второго. Выход-это два массива, которые четко определяют матрицу результатов: его (X,Y) координата равна 0, если координата X первого массива и координата Y второго равна 0.

обновление: в зависимости от языка, вы можете использовать некоторые хитрости, чтобы вернуть нормальный 2D-массив, ссылаясь на ту же строку несколько раз. Например в PHP:

// compute N-length arrays $X and $Y which have 1 at the column 
// and row positions which had no 1's in the input matrix
// this is O(M+N)
$result = array();
$row_one = array_fill(0,N,1);
for ($i=0; $i<N; $i++) {
    if ($Y[$i]) {
         $result[$i] = &$row_one;
    } else {
         $result[$i] = &$X;
    }
}
return $result;

конечно, это обычный массив, только пока вы не пытаетесь писать.


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

с небольшим дополнительным хранилищем 2*N вы можете выполнить операцию в O (N*N). Просто создайте маску для каждой строки и другую для каждого столбца-сканируйте массив и обновляйте маски по мере продвижения. Затем сканируйте снова, чтобы заполнить матрицу результатов на основе масок.

Если вы делаете что-то, где меняется входная матрица, вы можете сохранить количество ненулевых записей для каждая строка и столбец ввода (а не простая маска). Затем, когда запись во входных данных изменяется, вы соответствующим образом обновляете счетчики. В этот момент я бы полностью отбросил выходную матрицу и запросил маски / подсчеты напрямую, а не даже поддержал выходную матрицу (которая также может быть обновлена по мере изменения вещи менее чем за NN раз, если вы действительно хотите сохранить его). Таким образом, загрузка исходной матрицы все равно будет O (nN), но обновления могут быть намного меньше.


входная матрица может быть разреженной, но если вы не можете получить ее в разреженном формате (т. е. список (i,j) пары, которые изначально установлены), просто чтение вашего ввода будет потреблять Ω (n^2) Время. Даже при разреженном входе легко получить вывод O(n^2) для записи. В качестве обмана, если вам было разрешено выводить список заданных строк и столбцов, вы могли бы перейти к линейному времени. Нет никакой магии, когда ваш алгоритм на самом деле должен произвести результат более существенный, чем " да " или - нет.

комментарий Mcdowella к другому ответу предлагает другой альтернативный формат ввода: кодирование длины выполнения. Для разреженного ввода явно требуется не более O (n) времени для его чтения (рассмотрим, сколько переходов между 0 и 1). Однако оттуда он ломается. Рассмотрим входную матрицу, структурированную следующим образом:

0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 . . . 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . . .
.     .
.       .
.         .

то есть, чередуя 0 и 1 в первой строке, 0 везде. Явно разреженный, так как есть n/2 всего. Однако выход RLE должен повторять этот шаблон в каждой строке, что приводит к выходу O(n^2).


вы говорите:

мы должны получить результат как...

поэтому вам нужно вывести всю матрицу, которая имеет N^2 элементов. Это O (N*N).

сама проблема не O (N*N): вам не нужно вычислять и хранить всю матрицу: вам нужны только два вектора, L и C, каждый из размера N:

L[x] равно 1, Если линия x является линией единиц, 0 в противном случае;

C [x] равно 1, если строка x-строка из единиц, 0 в противном случае;

вы можете построить эти векторы в O(N), потому что начальная матрица разрежена; ваши входные данные будут не матрицей, а списком,содержащим координаты (строка, столбец) каждого ненулевого элемента. При чтении этого списка вы устанавливаете L[line]=1 и C[column]=1, и задача решается: M[l,c] = = 1, Если L[l]==1 или C[c]==1


хии ребята ,

благодаря комментарию от mb14 я думаю, что я мог бы решить его менее чем за O (NN) времени... В худшем случае O (NN)...

на самом деле , у нас есть данный массив предположим

  1 0 0 0 1
  0 1 0 0 0 
  0 1 1 0 0 
  1 1 1 0 1
  0 0 0 0 0 

позволяет иметь 2 массива размера N (это был бы худший случай) ... Один предназначен для индексирования строк и других столбцов... Поместите их с A[i] [1] = 0 в один массив, а затем a[1] [j] =0 в другой..

тогда берите только эти значения и проверьте вторую строку и colums...таким образом, мы получаем значения строк и colums, где есть только 0; полностью...

число значений в массиве строк дает число 0 в массиве результатов, а точки[значения массива строк][значение массива столбцов] дают вам эти точки ....

мы могли бы решить его ниже O (NN) и хуже всего O (nN)... Как мы можем видеть , массивы (размера N) уменьшаются ....

Я сделал это для нескольких массивы и получили результат для всех них ... :)

пожалуйста, поправьте меня, если я ошибаюсь в любом месте...

Thanx для всех ваших комментариев, ребята...Вы все очень полезно и я узнал довольно много вещей по пути ... :)


там явно до O(N^2) работа. В матрице

1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1

все биты должны быть установлены в 1, и N*(N-1) не установлены в один (20, в этом случае 5x5).

С другой стороны, вы можете придумать алгоритм, который всегда делает это O(N^2) time: sum вдоль верхней строки и столбца let, и если строка или столбец получает ненулевой ответ, заполните всю строку или столбец; затем решите меньшую (N-1)x(N-1) проблему.

Итак, существуют случаи, которые должны возьмите хотя бы N^2 и любой случай может быть решен в N^2 не требуется дополнительное пространство.


Если ваша матрица разрежена, сложность во многом зависит от входной кодировки и ее, в частности, не хорошо измеряется в N n2 или что-то в этом роде, но с точки зрения N вашей входной сложности Mнаи ваша Сложность вывода M out. Я ожидал бы чего-то вроде O(N + Mна + M out) но многое зависит от кодировки и трюков, которые вы можете играть с ним.


это полностью зависит от вашей структуры входных данных. Если вы передаете свою матрицу (1s и 0s) как 2D-массив, вам нужно пересечь ее, и это O(N^2). Но поскольку ваши данные разрежены, если вы передаете только 1 в качестве входных данных, вы можете сделать это так, чтобы O(M), где M-это не количество ячеек, а количество ячеек 1. Это было бы что-то похожее на это (псевдокод ниже):

list f(list l) {
   list rows_1;
   list cols_1;

    for each elem in l {
        rows_1[elem.row] = 1;
        cols_1[elem.col] = 1;
    }

    list result;
    for each row in rows_1 {
        for each col in cols_1 {
             if (row == 1 || col == 1) {
                 add(result, new_elem(row, col));
             }
        }
    } 
   return result;
}

Не заполняйте центр матрицы при проверке значений. По мере прохождения элементов, когда у вас есть 1, установите соответствующий элемент в первой строке и первом столбце. Затем возвращайтесь и наполняйте до краев.

edit: на самом деле это то же самое, что и у Энди.


Это зависит от вашей структуры данных.

есть только два возможных случая для строк:

  • строка i заполняется 1, если на входе есть элемент (i,_)
  • все остальные строки одинаковы: т. е. j-й элемент равен 1, если на входе есть элемент (_,j).

следовательно, результат может быть представлен компактно в виде массива ссылок на строки. Поскольку нам нужны только две строки, результат также будет потреблять только O (N) память. Например, это может быть реализовано в Python следующим образом:

def f(element_list, N):
  A = [1]*N
  B = [0]*N
  M = [B]*N
  for row, col in element_list:
    M[row] = A
    B[col] = 1
  return M

образец вызова будет

 f([(1,1),(2,2),(4,3)],5)

С результатом

[[0, 1, 1, 1, 0], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [0, 1, 1, 1, 0], [1, 1, 1, 1, 1]]

важным моментом является то, что массивы здесь не копируются, т. е. M[row]=A-это просто назначение ссылки. Следовательно, сложность равна O (N+M), где M-длина входного сигнала.


#include<stdio.h>

включить

тап_п() { int arr[5][5] = { {1,0,0,0,0}, {0,1,1,0,0}, {0,0,0,0,0}, {1,0,0,1,0}, {0,0,0,0,0} }; int var1=0, var2=0, i, j;

for(i=0;i<5;i++)
   var1 = var1 | arr[0][i];

for(i=0;i<5;i++)
   var2 = var2 | arr[i][0];

for(i=1;i<5;i++)
   for(j=1;j<5;j++)
      if(arr[i][j])
         arr[i][0] = arr[0][j] = 1;

for(i=1;i<5;i++)
   for(j=1;j<5;j++)
          arr[i][j] = arr[i][0] | arr[0][j];

for(i=0;i<5;i++)
   arr[0][i] = var1;

for(i=0;i<5;i++)
   arr[i][0] = var2;

for(i=0;i<5;i++)
{
   printf("\n");             
   for(j=0;j<5;j++)
      printf("%d ",arr[i][j]);
}

getch();

}

эта программа использует только 2 4 временные переменные (var1, var2, i и j) и, следовательно, работает в постоянном пространстве с временной сложностью O(n^2).. Я думаю, что вообще невозможно решить эту проблему в