Установите каждую ячейку в матрице в 0, если эта строка или столбец содержит 0


152

Дана матрица NxN с 0 и 1. Установите каждую строку, содержащую a 0для всех 0s, и установите каждый столбец, содержащий a, 0для всех 0s.

Например

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

результаты в

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

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

Кстати, представьте, что это битовая матрица, поэтому в матрице допускается только 1 и 0.


1
А? Что такое "всякий раз, когда вы сталкиваетесь"? В каком порядке вы встречаете элементы в матрице? И если вы столкнетесь со всеми битами, вы все равно не получите все 0?
ShreevatsaR

Ну, порядок, в котором вы решаете столкнуться с элементами, является вашим решением, дело в том, что вы должны установить только 0 соответствующих элементов. Если вы встретите все биты, установленные на 0, то да, матрица все равно будет заполнена нулями.
jaircazarin-old-account

Каковы "правильные элементы"? Вам даны две матрицы, одна матрица «источник» и одна матрица «цель», и вы должны решить, в каком порядке «встретить» элементы, чтобы получить «целевую» матрицу?
ShreevatsaR

1
Я думаю, что вы что-то не так поняли из-за «1 прохода». Это может быть сделано линейно за 2 прохода, но без дополнительной памяти, только 2 логических значений ;-) Поэтому я настоятельно полагаю, что это решение, которое он имел в виду (см. Ниже)
Петр Лесницкий

1
Не могли бы вы еще раз проверить с вашим другом, если описание проблемы действительно правильно? Я думал, что смогу сделать это с помощью кодов Хемминга или битов четности, но до сих пор я не добился успеха, и проблема продолжает накапливаться в моей голове. :)
csl

Ответы:


96

Итак, я устал, так как сейчас 3 часа утра, но у меня есть первая попытка на месте с ровно 2 проходами на каждое число в матрице, поэтому в O (NxN) он линейный по размеру матрицы.

Я использую 1-й столбец и первую строку в качестве маркеров, чтобы знать, где строки / столбцы только с 1. Затем есть 2 переменные l и c, которые нужно запомнить, если 1-я строка / столбец тоже все 1. Таким образом, первый проход устанавливает маркеры и сбрасывает остальные на 0.

Второй проход устанавливает 1 в местах, где строки и столбцы отмечены как 1, и сбрасывает 1-ю строку / столбец в зависимости от l и c.

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

import pprint

m = [[1, 0, 1, 1, 0],
     [0, 1, 1, 1, 0],
     [1, 1, 1, 1, 1],
     [1, 0, 1, 1, 1],
     [1, 1, 1, 1, 1]]



N = len(m)

### pass 1

# 1 rst line/column
c = 1
for i in range(N):
    c &= m[i][0]

l = 1
for i in range(1,N):
    l &= m[0][i]


# other line/cols
# use line1, col1 to keep only those with 1
for i in range(1,N):
    for j in range(1,N):
        if m[i][j] == 0:
            m[0][j] = 0
            m[i][0] = 0
        else:
            m[i][j] = 0

### pass 2

# if line1 and col1 are ones: it is 1
for i in range(1,N):
    for j in range(1,N):
        if m[i][0] & m[0][j]:
            m[i][j] = 1

# 1rst row and col: reset if 0
if l == 0:
    for i in range(N):
        m [i][0] = 0

if c == 0:
    for j in range(1,N):
        m [0][j] = 0


pprint.pprint(m)

Проблема здесь в том, что если n> sizeof (c), то он ломается. Чтобы расширить это для работы в общем случае n, вам нужно было бы динамически изменять размер вашего битового поля, что, я думаю, нарушило бы ограничение, заданное проблемой.
Адам

Нет, с не битфилд, это просто бул. Символ & = не является побитовой операцией (ну, это так, но для 1-битного значения), он существует потому, что c сообщает вам, является ли первый столбец 1 (true) или содержит 0 (false).
Стив Джессоп

2
Сбой, если верхняя строка имеет значение [0,1,1,1 ...]. Моя ошибка исправлена: инициализировать l вместо m [0] [0], а не 1
paperhorse

действительно, l = 1 для i в диапазоне (1, N): l & = m [0] [i] должно быть l = 1 для i в диапазоне (N): l & = m [0] [i]
Кристоф Нейринк

1
Кстати, я считаю, что условие во втором проходе должно быть что-то вроде: если m [i] [0] | m [0] [j]:
jaircazarin-old-account

16

Это невозможно сделать за один проход, поскольку один бит влияет на биты до и после него в любом порядке. IOW. В каком бы порядке вы ни проходили массив, позже вы можете пересечь 0, что означает, что вы должны вернуться назад и изменить предыдущее 1 на 0.

Обновить

Люди, кажется, думают, что, ограничив N некоторым фиксированным значением (скажем, 8), вы можете решить, что это один проход. Ну, это а) упущение и б) не оригинальный вопрос. Я бы не стал публиковать вопрос о сортировке и ожидать ответа, который начинался бы "предполагая, что вы хотите отсортировать только 8 вещей ...".

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


Это не может быть сделано за один проход без дополнительной памяти. Это можно сделать за один проход, если есть другая матрица NxN для сохранения результатов. Аналогично, с некоторыми битовыми твидами и двумя проходами это можно сделать без дополнительной памяти.
paxos1977

2
Вы все еще не можете сделать это за один проход, даже если вы используете временную матрицу, или есть что-то странное, чего я здесь не понимаю. Вам нужен один проход, чтобы получить информацию о строке / столбце, и один, чтобы установить все.
Лассе В. Карлсен

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

@ceretullis, lassevk: Я все еще думаю, что это невозможно сделать за один проход. Проходы по этой второй матрице должны быть учтены - в противном случае вы можете просто скопировать матрицу за один проход и работать с копией так, как вам хочется. @Daniel Papasian: Ваше решение не масштабируется там, где N> # битов в int / long / что угодно
Draemon

Draemon, техника отлично масштабируется, это просто математика - вы можете либо создать аппаратное обеспечение, которое это делает, либо вы можете использовать программные методы для манипулирования числами, превышающими размер вашего слова. Ни один не нарушает ограничения проблемы, ИМХО
Даниэль Папасян

10

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

Использование зигзагообразного сканирования по всей матрице за исключением последней строки / столбца. Для каждого элемента вы устанавливаете значение в последней строке / столбце как логическое И для самого себя со значением в текущем элементе. Другими словами, если вы нажмете 0, последняя строка / столбец будет установлена ​​на 0. Если вы это 1, значение в последней строке / столбце будет 1, только если он уже был 1. В любом случае установите текущий элемент на 0.

Когда вы закончите, ваша последняя строка / столбец будет иметь 1 с, если соответствующий столбец / строка была заполнена 1 с.

Выполните линейное сканирование последней строки и столбца и найдите 1 с. Установите 1 в соответствующих элементах в теле матрицы, где последняя строка и столбец равны 1.

Кодировать его будет непросто, чтобы избежать случайных ошибок и т. Д., Но это должно сработать за один проход.


Очень хорошо ... Я думал о тех же строках, но пропустил использование последней строки / столбца для хранения этой информации, поэтому я застрял с дополнительной памятью для пары массивов Nx1.
Дейв Шерохман

1
Это похоже на два прохода для меня - один проход - зигзагообразное сканирование, второй - «Установите 1-е в соответствующих элементах в теле матрицы, где последняя строка и столбец равны 1-му».
Адам Розенфилд

Зигзагообразное сканирование (которое, как мне кто-то указал, не является строго необходимым), пересекает все НО последний ряд / столбец. Таким образом, сканирование последнего столбца / строки не дублирует ранее отсканированные элементы. Отсюда один проход. Другими словами, это O (N ^ 2) для матрицы N * N.
Аластер

6

У меня есть решение, оно выполняется за один проход и выполняет всю обработку «на месте» без дополнительной памяти (за исключением увеличения стека).

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

#include <iostream>

/**
* The idea with my algorithm is to delay the writing of zeros
* till all rows and cols can be processed. I do this using
* recursion:
* 1) Enter Recursive Function:
* 2) Check the row and col of this "corner" for zeros and store the results in bools
* 3) Send recursive function to the next corner
* 4) When the recursive function returns, use the data we stored in step 2
*       to zero the the row and col conditionally
*
* The corners I talk about are just how I ensure I hit all the row's a cols,
* I progress through the matrix from (0,0) to (1,1) to (2,2) and on to (n,n).
*
* For simplicities sake, I use ints instead of individual bits. But I never store
* anything but 0 or 1 so it's still fair ;)
*/

// ================================
// Using globals just to keep function
// call syntax as straight forward as possible
int n = 5;
int m[5][5] = {
                { 1, 0, 1, 1, 0 },
                { 0, 1, 1, 1, 0 },
                { 1, 1, 1, 1, 1 },
                { 1, 0, 1, 1, 1 },
                { 1, 1, 1, 1, 1 }
            };
// ================================

// Just declaring the function prototypes
void processMatrix();
void processCorner( int cornerIndex );
bool checkRow( int rowIndex );
bool checkCol( int colIndex );
void zeroRow( int rowIndex );
void zeroCol( int colIndex );
void printMatrix();

// This function primes the pump
void processMatrix() {
    processCorner( 0 );
}

// Step 1) This is the heart of my recursive algorithm
void processCorner( int cornerIndex ) {
    // Step 2) Do the logic processing here and store the results
    bool rowZero = checkRow( cornerIndex );
    bool colZero = checkCol( cornerIndex );

    // Step 3) Now progress through the matrix
    int nextCorner = cornerIndex + 1;
    if( nextCorner < n )
        processCorner( nextCorner );

    // Step 4) Finially apply the changes determined earlier
    if( colZero )
        zeroCol( cornerIndex );
    if( rowZero )
        zeroRow( cornerIndex );
}

// This function returns whether or not the row contains a zero
bool checkRow( int rowIndex ) {
    bool zero = false;
    for( int i=0; i<n && !zero; ++i ) {
        if( m[ rowIndex ][ i ] == 0 )
            zero = true;
    }
    return zero;
}

// This is just a helper function for zeroing a row
void zeroRow( int rowIndex ) {
    for( int i=0; i<n; ++i ) {
        m[ rowIndex ][ i ] = 0;
    }
}

// This function returns whether or not the col contains a zero
bool checkCol( int colIndex ) {
    bool zero = false;
    for( int i=0; i<n && !zero; ++i ) {
        if( m[ i ][ colIndex ] == 0 )
            zero = true;
    }

    return zero;
}

// This is just a helper function for zeroing a col
void zeroCol( int colIndex ) {
    for( int i=0; i<n; ++i ) {
        m[ i ][ colIndex ] = 0;
    }
}

// Just a helper function for printing our matrix to std::out
void printMatrix() {
    std::cout << std::endl;
    for( int y=0; y<n; ++y ) {
        for( int x=0; x<n; ++x ) {
            std::cout << m[y][x] << " ";
        }
        std::cout << std::endl;
    }
    std::cout << std::endl;
}

// Execute!
int main() {
    printMatrix();
    processMatrix();
    printMatrix();
}

2
Хорошее решение, но технически вы используете больше памяти, чем два допустимых логических значения (хотя и в стеке).
csl

1
Это> 1 проход. Если вы напечатаете (rowIndex, i) и (i, colIndex) по мере их доступа в checkRow и checkCol, вы увидите, что к каждому элементу обращаются несколько раз.
Draemon

Draemon: Вы правы, я думаю, что нам нужно четкое определение «одного прохода» от создателя загадок. Если он действительно означает, что к каждому элементу можно получить доступ только один раз, тогда нам нужно другое решение.
Адам

Я предполагаю, что оригинальная проблема (которая пришла к нам через телефонную игру) означала, что проблема должна быть решена «на месте», то есть у вас нет другой копии матрицы. Более оптимальные решения даже не требуют временного свопинга (), например хранилища для обработки.
Адам

Также я сомневаюсь, что ограничения относятся к полученному машинному коду. Это означает, что «код», который я предоставил, использует только 2 bools. В зависимости от того, какие оптимизации выполняет мой компилятор, вся эта чертова вещь может быть встроена или кто знает, что еще. Я думаю, что мое решение является правильным;)
Адам

4

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


У меня почти такое же решение (см. Ниже) без дополнительных массивов. и это линейное время (но 2 прохода, хотя)
Петр Лесницкий

@ Петр: Да, второй проход кажется неизбежным. Предложенные мною массивы для хранения информации о строках и столбцах делают алгоритм более простым и немного более быстрым, поскольку меньше проверок и изменений значений. Это компромисс между хранением и эффективностью.
Боян

3

Я могу сделать это с двумя целочисленными переменными и двумя проходами (до 32 строк и столбцов ...)

bool matrix[5][5] = 
{ 
    {1, 0, 1, 1, 0},
    {0, 1, 1, 1, 0},
    {1, 1, 1, 1, 1},
    {1, 0, 1, 1, 1},
    {1, 1, 1, 1, 1}
};

int CompleteRows = ~0;
int CompleteCols = ~0;

// Find the first 0
for (int row = 0; row < 5; ++row)
{
    for (int col = 0; col < 5; ++col)
    {
        CompleteRows &= ~(!matrix[row][col] << row);
        CompleteCols &= ~(!matrix[row][col] << col);
    }
}

for (int row = 0; row < 5; ++row)
    for (int col = 0; col < 5; ++col)
        matrix[row][col] = (CompleteRows & (1 << row)) && (CompleteCols & (1 << col));

Это C #? Что значит ~?
Скер

Это C ++. ~инвертирует все биты в переменной. 0x00000000 становится 0x00000000. Я в основном начинаю со всех и очищаю бит для строки / столбца, когда нахожу 0. CompleteCols имеет установленные биты 2 и 3, а CompleteRows имеет установленные биты 2 и 4 (на основе 0).
Затмение

Затем вы просто устанавливаете биты в матрице, соответствующие единице в CompleteCols и CompleteRows.
Затмение

3

проблема может быть решена за один проход

сохранение матрицы в массиве i X j.

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

one each pass save the values of i and j for an element which is 0 in arrays a and b
when first row is scanned a= 1 b = 2,5
when second row is scanned a=1,2 b= 1,2,5
when third row is scanned no change
when fourth row is scanned a= 1,2,4 and b= 1,2,5
when fifth row is scanned no change .

теперь выведите все значения как 0 для значений i и j, сохраненных в a и b, остальные значения равны 1, т. е. (3,3) (3,4) (5,3) и (5,4)


1

Другое решение, которое занимает два прохода, заключается в накоплении AND по горизонтали и вертикали:

1 0 1 1 0 | 0
0 1 1 1 0 | 0
1 1 1 1 1 | 1
1 0 1 1 1 | 0
1 1 1 1 1 | 1
----------+
0 0 1 1 0    

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

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


1

Сохраняйте единственную переменную, чтобы отслеживать, каковы все строки ANDed вместе.

Если строка равна -1 (все 1 с), то сделайте следующую строку ссылкой на эту переменную

Если строка ничего, но это 0. Это можно сделать за один проход. Псевдопользователей-код:

foreach (my $row) rows {
     $andproduct = $andproduct & $row;
     if($row != -1) {
        zero out the row
     }  else {
        replace row with a reference to andproduct
     }
}

Это должно быть сделано за один проход - но здесь есть предположение, что N достаточно мало, чтобы процессор выполнял арифметику в одной строке, в противном случае вам нужно будет перебрать каждую строку, чтобы определить, все ли это 1с или нет, я верю. Но, учитывая, что вы спрашиваете об алгоритмах и не ограничиваете мое оборудование, я бы просто начал свой ответ с «Создание процессора, который поддерживает N-битную арифметику ...»

Вот один пример того, как это можно сделать на C. Примечание. Я утверждаю, что значения и arr, взятые вместе, представляют массив, а p и numproduct - это мои итераторы и переменные продукта AND, используемые для реализации проблемы. (Я мог зациклить arr с арифметикой указателя, чтобы проверить мою работу, но одного раза было достаточно!)

int main() {
    int values[] = { -10, 14, -1, -9, -1 }; /* From the problem spec, converted to decimal for my sanity */
    int *arr[5] = { values, values+1, values+2, values+3, values+4 };
    int **p;
    int numproduct = 127;

    for(p = arr; p < arr+5; ++p) {
        numproduct = numproduct & **p;
        if(**p != -1) {
            **p = 0;
        } else {
            *p = &numproduct;
        }
    }

    /* Print our array, this loop is just for show */
    int i;
    for(i = 0; i < 5; ++i) {
        printf("%x\n",*arr[i]);
    }
    return 0;
}

Это дает 0, 0, 6, 0, 6, что является результатом для заданных входов.

Или в PHP, если люди думают, что мои стековые игры в C обманывают (я полагаю, что это не так, потому что я должен иметь возможность хранить матрицу в любом удобном для меня виде):

<?php

$values = array(-10, 14, -1, -9, -1);
$numproduct = 127;

for($i = 0; $i < 5; ++$i) {
    $numproduct = $numproduct & $values[$i];
    if($values[$i] != -1) {
        $values[$i] = 0;
    } else {
        $values[$i] = &$numproduct;
    }
}

print_r($values);

Я что-то упускаю?


Это не работает, если N больше, чем число битов в int / long / что угодно, поэтому я не думаю, что это считается.
Draemon

Это также не поймает вещи, если 0 находятся в нижней части массива (попробуйте со значениями [] = {-1, -9, -1, 14, -10}).
Затмение

Draemon, я указываю в своем ответе, что без аппаратных ограничений, как часть вопроса, вы начинаете с «Создание процессора, который поддерживает N-битную арифметику».
Даниэль Папасян

Джош, я не следую С решением на C или PHP и предложенным вами массивом я получаю 6 0 6 0 0, и я считаю, что это правильный ответ.
Даниэль Папасян

@ Даниэль - Вы не можете, потому что N не является константой. Кроме того, «построить новый компьютер со словами 1 Мбит - вряд ли разумный алгоритмический шаг».
Draemon

1

Хороший вызов. Это решение использует только два логических значения, созданных в стеке, но логические значения создаются несколько раз в стеке, поскольку функция является рекурсивной.

typedef unsigned short     WORD;
typedef unsigned char      BOOL;
#define true  1
#define false 0
BYTE buffer[5][5] = {
1, 0, 1, 1, 0,
0, 1, 1, 1, 0,
1, 1, 1, 1, 1,
1, 0, 1, 1, 1,
1, 1, 1, 1, 1
};
int scan_to_end(BOOL *h,BOOL *w,WORD N,WORD pos_N)
{
    WORD i;
    for(i=0;i<N;i++)
    {
        if(!buffer[i][pos_N])
            *h=false;
        if(!buffer[pos_N][i])
            *w=false;
    }
    return 0;
}
int set_line(BOOL h,BOOL w,WORD N,WORD pos_N)
{
    WORD i;
    if(!h)
        for(i=0;i<N;i++)
            buffer[i][pos_N] = false;
    if(!w)
        for(i=0;i<N;i++)
            buffer[pos_N][i] = false;
    return 0;
}
int scan(int N,int pos_N)
{
    BOOL h = true;
    BOOL w = true;
    if(pos_N == N)
        return 0;
    // Do single scan
    scan_to_end(&h,&w,N,pos_N);
    // Scan all recursive before changeing data
    scan(N,pos_N+1);
    // Set the result of the scan
    set_line(h,w,N,pos_N);
    return 0;
}
int main(void)
{
    printf("Old matrix\n");
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[0][0],(WORD)buffer[0][1],(WORD)buffer[0][2],(WORD)buffer[0][3],(WORD)buffer[0][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[1][0],(WORD)buffer[1][1],(WORD)buffer[1][2],(WORD)buffer[1][3],(WORD)buffer[1][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[2][0],(WORD)buffer[2][1],(WORD)buffer[2][2],(WORD)buffer[2][3],(WORD)buffer[2][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[3][0],(WORD)buffer[3][1],(WORD)buffer[3][2],(WORD)buffer[3][3],(WORD)buffer[3][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[4][0],(WORD)buffer[4][1],(WORD)buffer[4][2],(WORD)buffer[4][3],(WORD)buffer[4][4]);
    scan(5,0);
    printf("New matrix\n");
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[0][0],(WORD)buffer[0][1],(WORD)buffer[0][2],(WORD)buffer[0][3],(WORD)buffer[0][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[1][0],(WORD)buffer[1][1],(WORD)buffer[1][2],(WORD)buffer[1][3],(WORD)buffer[1][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[2][0],(WORD)buffer[2][1],(WORD)buffer[2][2],(WORD)buffer[2][3],(WORD)buffer[2][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[3][0],(WORD)buffer[3][1],(WORD)buffer[3][2],(WORD)buffer[3][3],(WORD)buffer[3][4]);
    printf( "%d,%d,%d,%d,%d \n", (WORD)buffer[4][0],(WORD)buffer[4][1],(WORD)buffer[4][2],(WORD)buffer[4][3],(WORD)buffer[4][4]);
    system( "pause" );
    return 0;
}

Это сканирует по схеме, как:

s,s,s,s,s
s,0,0,0,0
s,0,0,0,0
s,0,0,0,0
s,0,0,0,0


0,s,0,0,0
s,s,s,s,s
0,s,0,0,0
0,s,0,0,0
0,s,0,0,0

и так далее

И затем изменение значений в этом шаблоне при возврате в каждую из функций сканирования. (Вверх дном):

0,0,0,0,c
0,0,0,0,c
0,0,0,0,c
0,0,0,0,c
c,c,c,c,c


0,0,0,c,0
0,0,0,c,0
0,0,0,c,0
c,c,c,c,c
0,0,0,c,0

и так далее


Я предполагаю, что это не правильно, так как вы все еще используете намного больше двух логических значений в вашем стеке.
CSL

Как я грустный вид двух логических выражений. Это самое близкое, что я могу придумать, к спецификациям, которые он предоставил. Я хотел бы увидеть фактическое решение здесь. Если это фесеско.
eaanon01

Я не думаю, что требования касаются роста стека. Я думаю, что это совершенно правильное решение.
Адам

Это тоже моя мысль. Но я не могу быть уверен, пока кто-то еще не отправит лучшее решение. По крайней мере, мое решение компилируемо и может быть проверено любым. :) ... Я не нашел псевдо-кода для практических задач. Thnx
eaanon01

1

Хорошо, это решение, которое

  • использует только одно дополнительное длинное значение для рабочего хранилища.
  • не использует рекурсию.
  • один проход только N, даже не N * N.
  • будет работать для других значений N, но потребуется новый #defines.
#include <stdio.h>
#define BIT30 (1<<24)
#define COLMASK 0x108421L
#define ROWMASK 0x1fL
unsigned long long STARTGRID = 
((0x10 | 0x0 | 0x4 | 0x2 | 0x0) << 20) |
((0x00 | 0x8 | 0x4 | 0x2 | 0x0) << 15) |
((0x10 | 0x8 | 0x4 | 0x2 | 0x1) << 10) |
((0x10 | 0x0 | 0x4 | 0x2 | 0x1) << 5) |
((0x10 | 0x8 | 0x4 | 0x2 | 0x1) << 0);


void dumpGrid (char* comment, unsigned long long theGrid) {
    char buffer[1000];
    buffer[0]='\0';
    printf ("\n\n%s\n",comment);
    for (int j=1;j<31; j++) {
        if (j%5!=1)
            printf( "%s%s", ((theGrid & BIT30)==BIT30)? "1" : "0",(((j%5)==0)?"\n" : ",") );    
        theGrid = theGrid << 1;
    }
}

int main (int argc, const char * argv[]) {
    unsigned long long rowgrid = STARTGRID;
    unsigned long long colGrid = rowgrid;

    unsigned long long rowmask = ROWMASK;
    unsigned long long colmask = COLMASK;

    dumpGrid("Initial Grid", rowgrid);
    for (int i=0; i<5; i++) {
        if ((rowgrid & rowmask)== rowmask) rowgrid |= rowmask;
        else rowgrid &= ~rowmask;
        if ((colGrid & colmask) == colmask) colmask |= colmask;
        else colGrid &=  ~colmask;
        rowmask <<= 5;
        colmask <<= 1;
    }
    colGrid &= rowgrid;
    dumpGrid("RESULT Grid", colGrid);
    return 0;
    }

Это хорошее решение, чтобы быть уверенным. И я полагаю, что каждое решение здесь пренебрегает хотя бы одним из требований. Таким образом, решение с максимально допустимым значением для N не самая плохая вещь в мире, поэтому неплохо с этим справиться :)
Адам,

Ограничение N до 8 и утверждение, что оно соответствует требованию одного прохода, просто глупо. Это не общее решение. В вопросе не было указано ограничение размера N, поэтому вы решили только подзадачу.
Draemon

но все эти решения так или иначе имеют ограничение на N.
Энтони Ламберт

Сказать, что это один проход N, очевидно, совершенно неправильно. Даже простое считывание значения каждой позиции в исходной матрице равно O (N ^ 2), и совершенно определенно необходимо прочитать значение в каждой позиции хотя бы один раз, чтобы иметь возможность вычислить решение. Даже если вы храните значения как отдельные биты в длинном long, доступ к каждому биту будет O (N ^ 2), потому что есть O (N ^ 2) бит.
Альдерат

Очевидно, что за один проход значение RowGrid сохраняет всю сетку, и после 1-го чтения будет один из регистров процессора для всего алгоритма, если оптимизатор хорош.
Энтони Ламберт

1

Фактически. Если вы просто хотите запустить алгоритм и распечатать результаты (т.е. не восстанавливать их, то это легко сделать за один проход. Проблема возникает, когда вы пытаетесь изменить массив во время работы алгоритма.

Вот мое решение. Оно просто включает в себя значения строк и столбцов AND для элемента givein (i, j) и распечатывает их.

#include <iostream>
#include "stdlib.h"

void process();

int dim = 5;
bool m[5][5]{{1,0,1,1,1},{0,1,1,0,1},{1,1,1,1,1},{1,1,1,1,1},{0,0,1,1,1}};


int main() {
    process();
    return 0;
}

void process() {
    for(int j = 0; j < dim; j++) {
        for(int i = 0; i < dim; i++) {
            std::cout << (
                          (m[0][j] & m[1][j] & m[2][j] & m[3][j] & m[4][j]) &
                          (m[i][0] & m[i][1] & m[i][2] & m[i][3] & m[i][4])
                          );
        }
        std::cout << std::endl;
    }
}

1

Я пытался решить эту проблему в C #.

Я использовал две переменные цикла (i и j) отдельно от фактической матрицы и n, представляющих ее размерность

Логика, которую я попробовал, заключается в следующем:

  1. Вычислить И для строк и столбцов, участвующих в каждом концентрическом квадрате матрицы
  2. Сохраните это в его угловых ячейках (я сохранил их против часовой стрелки)
  3. Две переменные bool используются для сохранения значений двух углов при оценке конкретного квадрата.
  4. Этот процесс заканчивается, когда внешний цикл (i) находится в середине пути.
  5. Оцените результаты других ячеек на основе угловых ячеек (для остальных я). Пропустите угловые ячейки во время этого процесса.
  6. Когда я достигну n, все ячейки будут иметь результат, кроме угловых.
  7. Обновите угловые ячейки. Это дополнительная итерация с длиной n / 2, отличная от одного проходного ограничения, упомянутого в задаче.

Код:

void Evaluate(bool [,] matrix, int n)
{
    bool tempvar1, tempvar2;

    for (var i = 0; i < n; i++)
    {
        tempvar1 = matrix[i, i];
        tempvar2 = matrix[n - i - 1, n - i - 1];

        var j = 0;

        for (j = 0; j < n; j++)
        {
            if ((i < n/2) || (((n % 2) == 1) && (i == n/2) && (j <= i)))
            {
                // store the row and col & results in corner cells of concentric squares
                tempvar1 &= matrix[j, i];
                matrix[i, i] &= matrix[i, j];
                tempvar2 &= matrix[n - j - 1, n - i - 1];
                matrix[n - i - 1, n - i - 1] &= matrix[n - i - 1, n - j - 1];
            }
            else
            {
                // skip corner cells of concentric squares
                if ((j == i) || (j == n - i - 1)) continue;

                // calculate the & values for rest of them
                matrix[i, j] = matrix[i, i] & matrix[n - j - 1, j];
                matrix[n - i - 1, j] = matrix[n - i - 1, n - i - 1] & matrix[n - j - 1, j];

                if ((i == n/2) && ((n % 2) == 1))
                {
                    // if n is odd
                    matrix[i, n - j - 1] = matrix[i, i] & matrix[j, n - j - 1];
                }
            }
        }

        if ((i < n/2) || (((n % 2) == 1) && (i <= n/2)))
        {
            // transfer the values from temp variables to appropriate corner cells of its corresponding square
            matrix[n - i - 1, i] = tempvar1;
            matrix[i, n - i - 1] = tempvar2;
        }
        else if (i == n - 1)
        {
            // update the values of corner cells of each concentric square
            for (j = n/2; j < n; j++)
            {
                tempvar1 = matrix[j, j];
                tempvar2 = matrix[n - j - 1, n - j - 1];

                matrix[j, j] &= matrix[n - j - 1, j];
                matrix[n - j - 1, j] &= tempvar2;

                matrix[n - j - 1, n - j - 1] &= matrix[j, n - j - 1];
                matrix[j, n - j - 1] &= tempvar1;
            }
        }
    }
}

1

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

public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        sc.nextLine();

        boolean rowDel = false, colDel = false;
        int arr[][] = new int[n][n];
        int res[][] = new int[n][n];
        int i, j;
        for (i = 0; i < n; i++) {

            for (j = 0; j < n; j++) {
                arr[i][j] = sc.nextInt();
                res[i][j] = arr[i][j];  
            }
        }

        for (i = 0; i < n; i++) {

            for (j = 0; j < n; j++) {
                if (arr[i][j] == 0)
                    colDel = rowDel = true; //See if we have to delete the
                                            //current row and column
                if (rowDel == true){
                    res[i] = new int[n];
                    rowDel = false;
                }
                if(colDel == true){
                    for (int k = 0; k < n; k++) {
                        res[k][j] = 0;
                    }
                    colDel = false;
                }

            }

        }

        for (i = 0; i < n; i++) {

            for (j = 0; j < n; j++) {
                System.out.print(res[i][j]);
            }
            System.out.println();
        }
        sc.close();

    }

0

Хотя это и невозможно, учитывая ограничения, наиболее эффективный способ сделать это - обойти матрицу в виде чередующейся чередующейся строки / столбца, что сделает шаблон похожим на укладку кирпичей зигзагообразным способом:

-----
|----
||---
|||--
||||-

Используя это, вы будете входить в каждую строку / столбец, как указано, и, если в любой момент вы встретите 0, установите логическую переменную и повторно пройдете эту строку / столбец, установив для записей значение 0 по мере продвижения.

Это не потребует дополнительной памяти и будет использовать только одну логическую переменную. К сожалению, если для «дальнего» края задано все 0, это наихудший случай, и вы дважды пройдете весь массив.


Я могу ошибаться, но вы уверены, что это работает? Когда вы делаете 3-й столбец, скажем, как вы узнали, было ли значение в верхней части, в первой строке, 1 или 0, прежде чем вы обработали первую строку?
Стив Джессоп

Вы не знаете, но вам также не нужно. Если это был 0, весь столбец должен быть 0. Если значение в предыдущей строке равно 1, вы знаете, что все строки над ним равны 1 (и всегда были).
Дейв Шерохман

0

создайте матрицу результатов и установите все значения на 1. Пройдите матрицу данных, как только встретите 0, установите столбец строки матрицы результатов на 0

В конце первого прохода матрица результатов будет иметь правильный ответ.

Выглядит довольно просто. Есть ли уловка, которую я пропускаю? Вам не разрешено использовать набор результатов?

РЕДАКТИРОВАТЬ:

Выглядит как функция F #, но это немного обманывает, так как даже если вы делаете один проход, функция может быть рекурсивной.

Похоже, что интервьюер пытается выяснить, знаете ли вы функциональное программирование.


1
Использование набора результатов заняло бы дополнительную память.
cdeszaq

Функциональное программирование не будет изменять исходный массив на месте.
Сванте

0

Ну, я придумал однопроходное, нерекурсивное решение на месте, используя 4 bools и 2 счетчика цикла. Мне не удалось сократить его до 2 bools и 2 ints, но я не удивлюсь, если бы это было возможно. Это делает приблизительно 3 чтения и 3 записи каждой ячейки, и это должно быть O (N ^ 2) т.е. линейный по размеру массива.

Мне потребовалось пару часов, чтобы разобраться в этом - я не хотел бы придумывать это под давлением интервью! Если я сделал буфу, я слишком устал, чтобы заметить это ...

Хм ... Я выбираю определение "однопроходного", чтобы сделать один проход по матрице, а не касаться каждого значения один раз! :-)

#include <stdio.h>
#include <memory.h>

#define SIZE    5

typedef unsigned char u8;

u8 g_Array[ SIZE ][ SIZE ];

void Dump()
{
    for ( int nRow = 0; nRow < SIZE; ++nRow )
    {
        for ( int nColumn = 0; nColumn < SIZE; ++nColumn )
        {
            printf( "%d ", g_Array[ nRow ][ nColumn ] );
        }
        printf( "\n" );
    }
}

void Process()
{
    u8 fCarriedAlpha = true;
    u8 fCarriedBeta = true;
    for ( int nStep = 0; nStep < SIZE; ++nStep )
    {
        u8 fAlpha = (nStep > 0) ? g_Array[ nStep-1 ][ nStep ] : true;
        u8 fBeta = (nStep > 0) ? g_Array[ nStep ][ nStep - 1 ] : true;
        fAlpha &= g_Array[ nStep ][ nStep ];
        fBeta &= g_Array[ nStep ][ nStep ];
        g_Array[ nStep-1 ][ nStep ] = fCarriedBeta;
        g_Array[ nStep ][ nStep-1 ] = fCarriedAlpha;
        for ( int nScan = nStep + 1; nScan < SIZE; ++nScan )
        {
            fBeta &= g_Array[ nStep ][ nScan ];
            if ( nStep > 0 )
            {
                g_Array[ nStep ][ nScan ] &= g_Array[ nStep-1 ][ nScan ];
                g_Array[ nStep-1][ nScan ] = fCarriedBeta;
            }

            fAlpha &= g_Array[ nScan ][ nStep ];
            if ( nStep > 0 )
            {
                g_Array[ nScan ][ nStep ] &= g_Array[ nScan ][ nStep-1 ];
                g_Array[ nScan ][ nStep-1] = fCarriedAlpha;
            }
        }

        g_Array[ nStep ][ nStep ] = fAlpha & fBeta;

        for ( int nScan = nStep - 1; nScan >= 0; --nScan )
        {
            g_Array[ nScan ][ nStep ] &= fAlpha;
            g_Array[ nStep ][ nScan ] &= fBeta;
        }
        fCarriedAlpha = fAlpha;
        fCarriedBeta = fBeta;
    }
}

int main()
{
    memset( g_Array, 1, sizeof(g_Array) );
    g_Array[0][1] = 0;
    g_Array[0][4] = 0;
    g_Array[1][0] = 0;
    g_Array[1][4] = 0;
    g_Array[3][1] = 0;

    printf( "Input:\n" );
    Dump();
    Process();
    printf( "\nOutput:\n" );
    Dump();

    return 0;
}

0

Я надеюсь, вам понравится мое 1-проходное решение C #

вы можете получить элемент с помощью O (1) и вам понадобится только место в одну строку и один столбец матрицы

bool[][] matrix =
{
    new[] { true, false, true, true, false }, // 10110
    new[] { false, true, true, true, false }, // 01110
    new[] { true, true, true, true, true },   // 11111
    new[] { true, false, true, true, true },  // 10111
    new[] { true, true, true, true, true }    // 11111
};

int n = matrix.Length;
bool[] enabledRows = new bool[n];
bool[] enabledColumns = new bool[n];

for (int i = 0; i < n; i++)
{
    enabledRows[i] = true;
    enabledColumns[i] = true;
}

for (int rowIndex = 0; rowIndex < n; rowIndex++)
{
    for (int columnIndex = 0; columnIndex < n; columnIndex++)
    {
        bool element = matrix[rowIndex][columnIndex];
        enabledRows[rowIndex] &= element;
        enabledColumns[columnIndex] &= element;
    }
}

for (int rowIndex = 0; rowIndex < n; rowIndex++)
{
    for (int columnIndex = 0; columnIndex < n; columnIndex++)
    {
        bool element = enabledRows[rowIndex] & enabledColumns[columnIndex];
        Console.Write(Convert.ToInt32(element));
    }
    Console.WriteLine();
}

/*
    00000
    00000
    00110
    00000
    00110
*/

Я думаю, что единственная проблема может заключаться в том, что вы используете два дополнительных массива данных для этой работы. Одним из условий является не использовать дополнительную память. Но хорошо, хотя! это в основном то, что я сделал в своем ответе :)
Кенни Кейсон

0

1 проход, 2 логических значения. Я просто должен предположить, что целочисленные индексы в итерациях не учитываются.

Это не полное решение, но я не могу пройти этот пункт.

Если бы я мог только определить, является ли 0 исходным 0 или 1, который был преобразован в 0, я бы не использовал -1, и это работало бы.

Мой вывод такой:

-1  0 -1 -1  0
 0 -1 -1 -1  0
-1 -1  1  1 -1
-1  0 -1 -1 -1
-1 -1  1  1 -1

Оригинальность моего подхода заключается в использовании первой половины проверки строки или столбца, чтобы определить, содержит ли она 0, а в последней половине - для установки значений - это делается путем просмотра x и width-x, а затем y и высоты -у в каждой итерации. Основываясь на результатах первой половины итерации, если в строке или столбце был найден 0, я использую последнюю половину итерации, чтобы изменить 1 на -1.

Я только что понял, что это можно сделать только с одним логическим значением, оставляя 1 для ...?

Я публикую это в надежде, что кто-то скажет: «А, просто сделай это ...» (И я потратил слишком много времени на это, чтобы не писать.)

Вот код в VB:

Dim D(,) As Integer = {{1, 0, 1, 1, 1}, {0, 1, 1, 0, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {0, 0, 1, 1, 1}}

Dim B1, B2 As Boolean

For y As Integer = 0 To UBound(D)

    B1 = True : B2 = True

    For x As Integer = 0 To UBound(D)

        // Scan row for 0's at x and width - x positions. Halfway through I'll konw if there's a 0 in this row.
        //If a 0 is found set my first boolean to false.
        If x <= (Math.Ceiling((UBound(D) + 1) / 2) - 1) Then
            If D(x, y) = 0 Or D(UBound(D) - x, y) = 0 Then B1 = False
        End If

        //If the boolean is false then a 0 in this row was found. Spend the last half of this loop
        //updating the values. This is where I'm stuck. If I change a 1 to a 0 it will cause the column
        //scan to fail. So for now I change to a -1. If there was a way to change to 0 yet later tell if
        //the value had changed this would work.
        If Not B1 Then
            If x >= (Math.Ceiling((UBound(D) + 1) / 2) - 1) Then
                If D(x, y) = 1 Then D(x, y) = -1
                If D(UBound(D) - x, y) = 1 Then D(UBound(D) - x, y) = -1
            End If
        End If

        //These 2 block do the same as the first 2 blocks but I switch x and y to do the column.
        If x <= (Math.Ceiling((UBound(D) + 1) / 2) - 1) Then
            If D(y, x) = 0 Or D(y, UBound(D) - x) = 0 Then B2 = False
        End If

        If Not B2 Then
            If x >= (Math.Ceiling((UBound(D) + 1) / 2) - 1) Then
                If D(y, x) = 1 Then D(y, x) = -1
                If D(y, UBound(D) - x) = 1 Then D(y, UBound(D) - x) = -1
            End If
        End If

    Next
Next

0

Никто не использует бинарные формы? так как это только 1 и 0. Мы можем использовать двоичные векторы.

def set1(M, N):
    '''Set 1/0s on M according to the rules.

    M is a list of N integers. Each integer represents a binary array, e.g.,
    000100'''
    ruler = 2**N-1
    for i,v in enumerate(M):
        ruler = ruler & M[i]
        M[i] = M[i] if M[i]==2**N-1 else 0  # set i-th row to all-0 if not all-1s
    for i,v in enumerate(M):
        if M[i]: M[i] = ruler
    return M

Вот тест:

M = [ 0b10110,
      0b01110,
      0b11111,
      0b10111,
      0b11111 ]

print "Before..."
for i in M: print "{:0=5b}".format(i)

M = set1(M, len(M))
print "After..."
for i in M: print "{:0=5b}".format(i)

И вывод:

Before...
10110
01110
11111
10111
11111
After...
00000
00000
00110
00000
00110

0

Вы можете сделать что-то вроде этого, чтобы использовать один проход, но матрицу ввода и вывода:

output(x,y) = col(xy) & row(xy) == 2^n

где col(xy)биты в столбце, содержащем точку xy; row(xy)это биты в строке, содержащей точку xy. nэто размер матрицы.

Затем просто зациклите ввод. Возможно расширяемый, чтобы быть более экономичным?


0

Одно матричное сканирование, два логических значения, без рекурсии.

Как избежать второго прохода? Второй проход необходим для очистки строк или столбцов, когда на их конце появляется ноль.

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

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

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

Чтобы не добавлять больше логических значений, мы храним флаг «очистить» для строк и столбцов в первой строке и столбце матрицы.

public void Run()
{
    const int N = 5;

    int[,] m = new int[N, N] 
                {{ 1, 0, 1, 1, 0 },
                { 1, 1, 1, 1, 0 },
                { 1, 1, 1, 1, 1 },
                { 1, 0, 1, 1, 1 },
                { 1, 1, 1, 1, 1 }};

    bool keepFirstRow = (m[0, 0] == 1);
    bool keepFirstColumn = keepFirstRow;

    for (int i = 1; i < N; i++)
    {
        keepFirstRow = keepFirstRow && (m[0, i] == 1);
        keepFirstColumn = keepFirstColumn && (m[i, 0] == 1);
    }

    Print(m); // show initial setup

    m[0, 0] = 1; // to protect first row from clearing by "second pass"

    // "second pass" is performed over i-1 row/column, 
    // so we use one more index just to complete "second pass" over the 
    // last row/column
    for (int i = 1; i <= N; i++)
    {
        for (int j = 1; j <= N; j++)
        {
            // "first pass" - searcing for zeroes in row/column #i
            // when i = N || j == N it is additional pass for clearing 
            // the previous row/column
            // j >= i because cells with j < i may be already modified 
            // by "second pass" part
            if (i < N && j < N && j >= i) 
            {
                m[i, 0] &= m[i, j];
                m[0, j] &= m[i, j];

                m[0, i] &= m[j, i];
                m[j, 0] &= m[j, i];
            }

            // "second pass" - clearing the row/column scanned 
            // in the previous iteration
            if (m[i - 1, 0] == 0 && j < N)
            {
                m[i - 1, j] = 0;
            }

            if (m[0, i - 1] == 0 && j < N)
            {
                m[j, i - 1] = 0;
            }
        }

        Print(m);
    }

    // Clear first row/column if needed
    if (!keepFirstRow || !keepFirstColumn)
    {
        for (int i = 0; i < N; i++)
        {
            if (!keepFirstRow)
            {
                m[0, i] = 0;
            }
            if (!keepFirstColumn)
            {
                m[i, 0] = 0;
            }
        }
    }

    Print(m);

    Console.ReadLine();
}

private static void Print(int[,] m)
{
    for (int i = 0; i < m.GetLength(0); i++)
    {
        for (int j = 0; j < m.GetLength(1); j++)
        {
            Console.Write(" " + m[i, j]);
        }
        Console.WriteLine();
    }
    Console.WriteLine();
}

0

Кажется, что следующие работы без дополнительных требований к пространству:

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

Чтобы не использовать дополнительное пространство (не создавая новую матрицу и не заполняя ее, а вместо этого применяя изменения непосредственно к матрице), начните с левого верхнего угла матрицы и выполните вычисления для любой матрицы ixi (которая «начинается» в (0 , 0)) перед рассмотрением любого элемента с любым индексом> i.

надеюсь, что это работает (не тестет)


Кажется, неправильно. Предположим, что строка 0 имеет только 1 значения. Если конечное значение, которое вы установили для (0,0), равно 0, то позже вы установите полную строку в 0, что не всегда верно. На самом деле вам нужно хранить 2 значения на ячейку, чтобы сделать это в стиле динамического программирования, используя ваш принцип.
Эяль Шнайдер

конечно, ты прав. Вместо того, чтобы хранить два значения, я мог бы также использовать третью возможность, скажем, -1, которая обозначает ячейки, которые равны 1 в «старой» матрице, которая в конечном итоге будет заменена на 0. Конечно, таким образом, нужно принять абсолютное значения после умножения. В конце все -1 заменяются на 0.
DFF

0

Это ИСПЫТАНО для разных N в C ++, и это:
ОДИН ПРОХОД , ДВЕ БУЛЫ , НЕТ РЕКУРСИИ , НЕТ ДОПОЛНИТЕЛЬНОЙ ПАМЯТИ , ХОЛДОВ ДЛЯ АРБИТРАЛЬНО БОЛЬШИХ N
(Пока ни одно из решений здесь не делает ВСЕ из них.)

Точнее говоря, я забавно, что два счетчика цикла в порядке. У меня есть два константных знака без знака, которые только существуют, а не вычисляются каждый раз для удобства чтения. Интервал внешнего цикла равен [0, N], а интервал внутреннего цикла равен [1, n - 1]. Оператор switch в цикле в основном существует, чтобы очень четко показать, что на самом деле это всего лишь один проход.

Алгоритм Стратегия:

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

Алгоритм Сводка:

  • Просканируйте первую строку и сохраните, если все они в логическом значении, сделайте то же самое для первого столбца, сохраняя результат во втором логическом значении.
  • Для подматрицы, исключая первую строку и первый столбец: перебирайте, слева направо, сверху вниз, как при прочтении абзаца. При посещении каждого элемента также посетите соответствующий элемент, который будет посещен при обратном посещении подматрицы. Для каждого посещенного элемента И его значение в том месте, где его строка пересекает первый столбец, а также И его значение в том месте, где его столбец пересекает первую строку.
  • Как только будет достигнут центр субматрицы, продолжайте посещать два элемента одновременно, как указано выше. Однако теперь установите для посещаемых элементов значение AND, где его строка пересекает первый столбец, и где его столбец пересекает первую строку. После этого субматрица завершена.
  • Используйте две логические переменные, вычисленные в начале, чтобы установить для первой строки и первого столбца их правильные значения.

Шаблонная реализация C ++:

template<unsigned n>
void SidewaysAndRowColumn(int((&m)[n])[n]) {
    bool fcol = m[0][0] ? true : false;
    bool frow = m[0][0] ? true : false;
    for (unsigned d = 0; d <= n; ++d) {
        for (unsigned i = 1; i < n; ++i) {
            switch (d) {
                case 0:
                    frow    = frow && m[d][i];
                    fcol    = fcol && m[i][d];
                    break;
                default:
                {
                    unsigned const rd = n - d;
                    unsigned const ri = n - i;
                    if (d * n + i < rd * n + ri)
                    {
                        m[ d][ 0] &= m[ d][ i];
                        m[ 0][ d] &= m[ i][ d];
                        m[ 0][ i] &= m[ d][ i];
                        m[ i][ 0] &= m[ i][ d];
                        m[rd][ 0] &= m[rd][ri];
                        m[ 0][rd] &= m[ri][rd];
                        m[ 0][ri] &= m[rd][ri];
                        m[ri][ 0] &= m[ri][rd];
                    }
                    else
                    {
                        m[ d][ i] = m[ d][0] & m[0][ i];
                        m[rd][ri] = m[rd][0] & m[0][ri];
                    }
                    break;
                }
                case n:
                    if (!frow)
                        m[0][i] = 0;
                    if (!fcol)
                        m[i][0] = 0;
            };
        }
    }
    m[0][0] = (frow && fcol) ? 1 : 0;
}

0

Хорошо, я понимаю, что это не совсем совпадает, но я получил его за один проход, используя bool и байт вместо двух bools ... close. Я также не ручаюсь за его эффективность, но такие вопросы часто требуют неоптимальных решений.

private static void doIt(byte[,] matrix)
{
    byte zeroCols = 0;
    bool zeroRow = false;

    for (int row = 0; row <= matrix.GetUpperBound(0); row++)
    {
        zeroRow = false;
        for (int col = 0; col <= matrix.GetUpperBound(1); col++)
        {
            if (matrix[row, col] == 0)
            {

                zeroRow = true;
                zeroCols |= (byte)(Math.Pow(2, col));

                // reset this column in previous rows
                for (int innerRow = 0; innerRow < row; innerRow++)
                {
                    matrix[innerRow, col] = 0;
                }

                // reset the previous columns in this row
                for (int innerCol = 0; innerCol < col; innerCol++)
                {
                    matrix[row, innerCol] = 0;
                }
            }
            else if ((int)(zeroCols & ((byte)Math.Pow(2, col))) > 0)
            {
                matrix[row, col] = 0;
            }

            // Force the row to zero
            if (zeroRow) { matrix[row, col] = 0; }
        }
    }
}

0

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

[править: простое, но неправильное решение удалено]

Вы должны получить лучшую производительность, чем любой однопроходный метод, выполнив это за два прохода: один для накопления информации о строке / столбце и один для его применения. Доступ к массиву (в порядке следования строк) связан; для массивов, превышающих размер кэша (но строки которых могут помещаться в кэш), данные должны быть прочитаны из памяти дважды и сохранены один раз:

void fixmatrix2(int M[][], int rows, int cols) {
    bool clearZeroRow= false;
    bool clearZeroCol= false;
    for(int j=0; j < cols; ++j) {
        if( ! M[0][j] ) {
            clearZeroRow= true;
        }
    }
    for(int i=1; i < rows; ++i) { // scan/accumulate pass
        if( ! M[i][0] ) {
            clearZeroCol= true;
        }
        for(int j=1; j < cols; ++j) {
            if( ! M[i][j] ) {
                M[0][j]= 0;
                M[i][0]= 0;
            }
        }
    }
    for(int i=1; i < rows; ++i) { // update pass
        if( M[i][0] ) {
            for(int j=0; j < cols; ++j) {
                if( ! M[j][0] ) {
                    M[i][j]= 0;
                }
            }
        } else {
            for(int j=0; j < cols; ++j) {
                M[i][j]= 0;
            }
        }
        if(clearZeroCol) {
            M[i][0]= 0;
        }
    }
    if(clearZeroRow) {
        for(int j=0; j < cols; ++j) {
            M[0][j]= 0;
        }
    }
}

0

Самое простое решение, которое я могу придумать, приведено ниже. Логика заключается в том, чтобы записать, какую строку и столбец установить на ноль во время итерации.

import java.util.HashSet;
import java.util.Set;

public class MatrixExamples {
    public static void zeroOut(int[][] myArray) {
        Set<Integer> rowsToZero = new HashSet<>();
        Set<Integer> columnsToZero = new HashSet<>();

        for (int i = 0; i < myArray.length; i++) { 
            for (int j = 0; j < myArray.length; j++) {
                if (myArray[i][j] == 0) {
                    rowsToZero.add(i);
                    columnsToZero.add(j);
                }
            }
        }

        for (int i : rowsToZero) {
            for (int j = 0; j < myArray.length; j++) {
                myArray[i][j] = 0;
            }
        }

        for (int i : columnsToZero) {
            for (int j = 0; j < myArray.length; j++) {
                myArray[j][i] = 0;
            }
        }

        for (int i = 0; i < myArray.length; i++) { // record which rows and                                             // columns will be zeroed
            for (int j = 0; j < myArray.length; j++) {
                System.out.print(myArray[i][j] + ",");
            if(j == myArray.length-1)
                System.out.println();
            }
        }

    }

    public static void main(String[] args) {
        int[][] a = { { 1, 0, 1, 1, 0 }, { 0, 1, 1, 1, 0 }, { 1, 1, 1, 1, 1 },
                { 1, 0, 1, 1, 1 }, { 1, 1, 1, 1, 1 } };
        zeroOut(a);
    }
}

0

Вот моя реализация Ruby с включенным тестом. Это заняло бы пространство O (MN). Если нам нужно обновление в реальном времени (например, показ результатов, когда мы находим нули, а не ожидание первого цикла нахождения нулей), мы можем просто создать другую переменную класса, такую ​​как @outputи всякий раз, когда мы находим ноль, мы обновляем @outputи нет @input.

require "spec_helper"


class Matrix
    def initialize(input)
        @input  = input
        @zeros  = []
    end

    def solve
        @input.each_with_index do |row, i|          
            row.each_with_index do |element, j|                             
                @zeros << [i,j] if element == 0
            end
        end

        @zeros.each do |x,y|
            set_h_zero(x)
            set_v_zero(y)
        end

        @input
    end


    private 

    def set_h_zero(row)     
        @input[row].map!{0}     
    end

    def set_v_zero(col)
        @input.size.times do |r|
            @input[r][col] = 0
        end
    end
end


describe "Matrix" do
  it "Should set the row and column of Zero to Zeros" do
    input =  [[1, 3, 4, 9, 0], 
              [0, 3, 5, 0, 8], 
              [1, 9, 6, 1, 9], 
              [8, 3, 2, 0, 3]]

    expected = [[0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0],
                [0, 9, 6, 0, 0],
                [0, 0, 0, 0, 0]]

    matrix = Matrix.new(input)

    expect(matrix.solve).to eq(expected)
  end
end

0

Код ниже создает матрицу размером m, n. Сначала определитесь с размерами матрицы. Я хотел заполнить матрицу [m] [n] случайным образом числами от 0,10. Затем создайте другую матрицу с такими же размерами и заполните ее -1 с (конечная матрица). Затем выполните итерацию по исходной матрице, чтобы увидеть, достигнете ли вы 0. Когда вы нажмете на местоположение (x, y), перейдите к финальной матрице и заполните строку x 0 и столбец y 0. В конце прочитайте окончательную матрицу, если значение равно -1 (исходное значение), скопируйте значение в том же месте исходной матрицы в конечную.

public static void main(String[] args) {
    int m = 5;
    int n = 4;
    int[][] matrixInitial = initMatrix(m, n); // 5x4 matrix init randomly
    int[][] matrixFinal = matrixNull(matrixInitial, m, n); 
    for (int i = 0; i < m; i++) {
        System.out.println(Arrays.toString(matrixFinal[i]));
    }
}

public static int[][] matrixNull(int[][] matrixInitial, int m, int n) {
    int[][] matrixFinal = initFinal(m, n); // create a matrix with mxn and init it with all -1
    for (int i = 0; i < m; i++) { // iterate in initial matrix
        for (int j = 0; j < n; j++) {
            if(matrixInitial[i][j] == 0){ // if a value is 0 make rows and columns 0
                makeZeroX(matrixFinal, i, j, m, n); 
            }
        }
    }

    for (int i = 0; i < m; i++) { // if value is -1 (original) copy from initial
        for (int j = 0; j < n; j++) {
            if(matrixFinal[i][j] == -1) {
                matrixFinal[i][j] = matrixInitial[i][j];
            }
        }
    }
    return matrixFinal;
}

private static void makeZeroX(int[][] matrixFinal, int x, int y, int m, int n) {
        for (int j = 0; j < n; j++) { // make all row 0
            matrixFinal[x][j] = 0;
        }
        for(int i = 0; i < m; i++) { // make all column 0
            matrixFinal[i][y] = 0; 
        }
}

private static int[][] initMatrix(int m, int n) {

    int[][] matrix = new int[m][n];
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            Random rn = new Random();
            int random = rn.nextInt(10);
            matrix[i][j] = random;
        }
    }

    for (int i = 0; i < m; i++) {
        System.out.println(Arrays.toString(matrix[i]));
    }
    System.out.println("******");
    return matrix;
}

private static int[][] initFinal(int m, int n) {

    int[][] matrix = new int[m][n];
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            matrix[i][j] = -1;
        }
    }
    return matrix;
}

// another approach
/**
 * @param matrixInitial
 * @param m
 * @param n
 * @return
 */
private static int[][] matrixNullNew(int[][] matrixInitial, int m, int n) {
    List<Integer> zeroRowList = new ArrayList<>(); // Store rows with 0
    List<Integer> zeroColumnList = new ArrayList<>(); // Store columns with 0
    for (int i = 0; i < m; i++) { // read through the matrix when you hit 0 add the column to zeroColumnList and add
                                  // the row to zeroRowList
        for (int j = 0; j < n; j++) {
            if (matrixInitial[i][j] == 0) {
                if (!zeroRowList.contains(i)) {
                    zeroRowList.add(i);
                }
                if (!zeroColumnList.contains(j)) {
                    zeroColumnList.add(j);
                }
            }
        }
    }

    for (int a = 0; a < m; a++) {
        if (zeroRowList.contains(a)) { // if the row has 0
            for (int b = 0; b < n; b++) {
                matrixInitial[a][b] = 0; // replace all row with zero
            }
        }
    }

    for (int b = 0; b < n; b++) {
        if (zeroColumnList.contains(b)) { // if the column has 0
            for (int a = 0; a < m; a++) {
                matrixInitial[a][b] = 0; // replace all column with zero
            }
        }
    }
    return matrixInitial;
}

Вы не даете никакого объяснения или контекста к опубликованному вами коду.
Аарон

Надеюсь, теперь это лучше. Спасибо за предупреждение. Я хотел бы объяснить больше, если это не ясно для вас.
user3743369

0

вот мое решение. Как вы можете видеть из кода, учитывая матрицу M * N, он устанавливает всю строку в ноль, как только проверяет ноль в этой строке. Временная сложность моего решения - O (M * N). Я делюсь всем классом, который имеет статический заполненный массив для тестирования и метод отображения, чтобы увидеть результат в консоли.

public class EntireRowSetToZero {
    static int arr[][] = new int[3][4];
    static {

    arr[0][0] = 1;
    arr[0][1] = 9;
    arr[0][2] = 2;
    arr[0][3] = 2;

    arr[1][0] = 1;
    arr[1][1] = 5;
    arr[1][2] = 88;
    arr[1][3] = 7;

    arr[2][0] = 0;
    arr[2][1] = 8;
    arr[2][2] = 4;
    arr[2][3] = 4;
}

public static void main(String[] args) {
    displayArr(EntireRowSetToZero.arr, 3, 4);
    setRowToZero(EntireRowSetToZero.arr);
    System.out.println("--------------");
    displayArr(EntireRowSetToZero.arr, 3, 4);


}

static int[][] setRowToZero(int[][] arr) {
    for (int i = 0; i < arr.length; i++) {
        for (int j = 0; j < arr[i].length; j++) {
            if(arr[i][j]==0){
                arr[i]=new int[arr[i].length];
            }
        }

    }
    return arr;
}

static void displayArr(int[][] arr, int n, int k) {

    for (int i = 0; i < n; i++) {

        for (int j = 0; j < k; j++) {
            System.out.print(arr[i][j] + " ");
        }
        System.out.println("");
    }

}

}

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.