Встановіть для кожної комірки в матриці 0, якщо цей рядок або стовпець містить 0


152

Дано матрицю NxN з 0s та 1s. Встановіть кожен рядок, який містить 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 сказав мені, що існує рішення, яке не потребує додаткової пам'яті, лише дві булеві змінні та один прохід, тому я шукаю цю відповідь.

До речі, уявіть, що це бітова матриця, тому лише 1s і 0s дозволяють знаходитись у матриці.


1
Так? Що таке "щоразу, коли стикаєшся"? У якому порядку ви стикаєтеся з елементами в матриці? І якщо ви зіткнетесь з усіма бітами, чи не отримаєте ви все-таки 0?
ShreevatsaR

Що ж, порядок, в якому ви вирішили зіткнутися з елементами, це ваше рішення, річ у тому, що ви повинні встановити лише 0s належних елементів. Якщо ви зіткнулися з усіма бітами, встановленими на 0, так, матриця все одно буде заповнена нулями.
jaircazarin-old-account

Що таке "належні елементи"? Чи дано вам дві матриці, одну матрицю "джерела" та одну матрицю "ціль", і ви повинні вирішити, в якому порядку "стикатися" з елементами, щоб отримати "цільову" матрицю?
ShreevatsaR

1
Я думаю, що ти щось неправильно почув для думки "1 пропуск". Це можна зробити лінійно за 2 проходи, хоча без додаткової пам’яті всього 2 булеви ;-) Тож я напевно припускаю, що саме це рішення він мав на увазі (див. Нижче)
Пьотр Лесницький

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

Відповіді:


96

Гаразд, я втомився, оскільки тут 3 ранку, але в мене є перша спроба замість того, щоб рівно 2 проходи на кожне число в матриці, так що в O (NxN), і це лінійно за розміром матриці.

Я використовую перший стовпчик і перший рядок як маркери, щоб знати, де знаходяться рядки / знаки з лише 1. Тоді є дві змінні l і c, щоб пам’ятати, чи є також перший рядок / стовпець 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
папір для коней

дійсно 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> #bits в int / long / як би то не було
Draemon

Draemon, техніка масштабує добре, це просто математика - ви можете створити обладнання, яке це робить, або ви можете використовувати програмні прийоми для маніпулювання числами, більшими за розмір слова. Не порушує
жодних

10

Тож моя ідея полягає у використанні значень в останньому рядку / стовпчику як прапор, щоб вказати, чи всі значення у відповідному стовпчику / рядку є 1s.

Використовуючи сканування Zig Zag через всю матрицю, ОКРЕМИ заключний рядок / стовпець. На кожному елементі ви встановлюєте значення в кінцевому рядку / стовпчику як логічне І самого себе зі значенням у поточному елементі. Іншими словами, якщо ви потрапили на 0, для кінцевого рядка / стовпця буде встановлено 0. Якщо ви це 1, значення в кінцевому рядку / стовпці буде 1, лише якщо воно вже було 1. У будь-якому випадку встановіть поточний елемент на 0.

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

Зробіть лінійне сканування через заключний рядок і стовпець і шукайте 1s. Встановіть 1s у відповідні елементи тіла матриці, де кінцевий рядок та стовпець є 1s.

Кодування це буде складно, щоб уникнути випадкових помилок тощо, але це повинно працювати за один прохід.


Дуже приємно ... Я думав у тих же рядках, але пропустив використання остаточного рядка / стовпця для зберігання цієї інформації, тому я застряг із додатковою пам’яттю для пари масивів Nx1.
Дейв Шерохман

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

Дреймон: Ви праві, я думаю, нам потрібне чітке визначення "єдиного проходу" від виробника загадок. Якщо він справді означає, що до кожного елемента можна отримати доступ лише один раз, тоді нам потрібно інше рішення.
Адам

Я думаю, що початкова проблема (яка надійшла до нас за допомогою телефонної гри) означала, що проблему слід вирішити "на місці", тобто у вас немає іншої копії матриці. А більш оптимальні рішення навіть не потребують тимчасового swap (), як сховище для обробки.
Адам

Крім того, я сумніваюся, що обмеження посилаються на результат машинного коду. Значить, "код", який я надав, використовує лише 2 булі. Залежно від того, які оптимізації робить мій компілятор, вся ця чортова річ може бути окреслена чи хто ще знає. Я вважаю, що моє рішення правильне;)
Адам

4

Я не думаю, що це можливо. Коли ви знаходитесь на першому квадраті, а його значення дорівнює 1, ви не можете знати, якими є значення інших квадратів у тому ж рядку та стовпці. Таким чином, ви повинні перевірити їх, і якщо є нуль, повернутися до першого квадрата і змінити його значення на нуль. Я рекомендую зробити це за два проходи - перший пропуск збирає інформацію про те, які рядки та стовпці повинні бути нульовими (інформація зберігається в масиві, тому ми використовуємо додаткову пам'ять). Другий прохід змінює значення. Я знаю, що це не рішення, яке ви шукаєте, але я думаю, що це практичне. Надані вами обмеження роблять проблему нерозв'язною.


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

@Piotr: Так, другий прохід видається неминучим. Введення масивів для зберігання інформації про рядки та стовпці, що я запропонував, робить алгоритм більш прямим і трохи швидшим, оскільки менше перевірки та зміни цінності. Це компроміс між зберіганням та ефективністю.
Боян

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 #? Що означає ~
sker

Це 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

Іншим рішенням, яке займає два проходи, є накопичення ANDs по горизонталі та вертикалі:

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 (всі 1s), тоді слід зробити наступний рядок посиланням на цю змінну

Якщо ряд - це що завгодно, але це 0. Ви можете зробити все за один прохід. Псуедо-код:

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

Це повинно робити це за один прохід - але тут є припущення, що N достатньо малий, щоб ЦП може робити арифметику в одному рядку, інакше вам потрібно буде перебирати кожен рядок, щоб визначити, чи все це Я вважаю, що це ні, чи ні. Але враховуючи, що ви запитуєте про algos і не обмежуєте моє обладнання, я би просто почав свою відповідь із "Створення CPU, який підтримує 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, я вказую у своїй відповіді, що без апаратних обмежень як частини питання, ви починаєте з "Створення CPU, який підтримує N-бітну арифметику".
Даніель Папасян

Джош, я не слідую за цим. З рішенням C або PHP та масивом, який ви запропонували, я отримую 6 0 6 0 0, на який я вважаю, що це правильна відповідь.
Даніель Папасян

@Daniel - Ти не можеш, бо N - не константа. Окрім того, "побудувати новий комп'ютер із 1Mbit слів навряд чи є розумним алгоритмічним кроком"
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.
AnthonyLambert

Сказати, що це один прохід N, очевидно, абсолютно неправильно. Навіть просто зчитування значення кожної позиції в початковій матриці є O (N ^ 2), і, безумовно, необхідно прочитати значення в кожній позиції хоча б один раз, щоб мати можливість обчислити рішення. Навіть якщо ви зберігаєте значення як одиничні біти протягом довгого часу, доступ до кожного біта буде O (N ^ 2), оскільки є O (N ^ 2) біт.
Alderath

Очевидно, що за один прохід значення RowGrid зберігає всю сітку, і після першого читання буде одним з регістрів процесорів для всього алгоритму, якщо оптимізатор буде корисним.
AnthonyLambert

1

Насправді. Якщо ви просто хочете запустити алгоритм і роздрукувати результати (тобто не відновити їх, то це можна легко зробити за один прохід. Проблема виникає при спробі змінити масив під час роботи алгоритму.

Ось моє рішення, воно просто включає ANDing значення рядків / стовпців для елемента 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. Оцініть результати інших комірок на основі кутових комірок (для решти i). Пропустіть кутові комірки під час цього процесу.
  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

Функціональне програмування не змінило б оригінальний масив на місці.
Svante

0

Ну, я придумав однопрохідне, немісцеве (нерекурсивне) рішення, використовуючи 4 булі та 2 лічильника циклу. Мені не вдалося зменшити його до 2-х булів та 2-х точок, але я не здивуюсь, якщо б це було можливо. Це робить приблизно 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 та висоту -y в кожній ітерації. На основі результатів першої половини ітерації, якщо знайдено 0 у рядку чи стовпці, я використовую останню половину ітерації, щоб змінити значення 1 на -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 значення на комірку, щоб зробити це стилем динамічного програмування, використовуючи свій принцип.
Eyal Schneider

впевнений, ти маєш рацію. Замість того, щоб зберігати два значення, я також міг би використовувати третю позитивність, скажімо, -1, що означає клітинки, що є "в" старій "матриці, яка в кінцевому підсумку буде замінена на 0. Звичайно, таким чином треба взяти абсолютний значення після множення. Зрештою, всі -1 замінюються на 0.
DFF

0

Це випробувати для різних N в C ++, а це:
один прохід , ДВА Bools , НІ RECURSION , НІ додаткова пам'ять , виконується для ARBITRARLY ВЕЛИКОГО N
( До сих пір жоден з рішень тут роблять всі ці.)

Більш конкретно, я забавляюся, що два лічильника циклу все в порядку. У мене є два непідписані const, які існують, а не щоразу обчислюються для читабельності. Інтервал зовнішньої петлі дорівнює [0, N], а інтервал внутрішньої петлі - [1, n - 1]. Заява перемикання знаходиться в циклі, здебільшого існує, щоб дуже чітко показати, що це дійсно лише один прохід.

Алгоритм стратегії:

Перший трюк - це нам рядок і стовпчик з самої матриці, щоб накопичити вміст матриці, ця пам'ять стає доступною, завантажуючи все, що ми насправді потрібно знати, з першого рядка та стовпця на два булеві. Другий трюк - отримати два проходи з одного, використовуючи симетрію підматриці та її індекси.

Аналіз алгоритму:

  • Скануйте перший рядок і збережіть, якщо вони всі булеві, зробіть те ж саме для першого стовпця, який зберігає результат у другому булевому.
  • Для підматриці, що виключає перший рядок та перший стовпець: повторіть, зліва направо, зверху вниз, як читати абзац. Відвідавши кожен елемент, також відвідайте відповідний елемент, який був би відвіданий, якщо відвідати підматрицю в зворотному порядку. Для кожного відвідуваного елемента І його значення в тому місці, де його рядок перетинає перший стовпець, а також його значення, де його стовпець перетинає перший рядок.
  • Після досягнення центру підматриці продовжуйте відвідувати два елементи одночасно, як вище. Однак тепер встановіть значення відвідуваних елементів значення 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 ... закрити. Я також не хотів би посягати на ефективність роботи, але такі питання часто вимагають менш оптимальних рішень.

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. Потім створіть іншу матрицю таких же розмірів і заповніть її -1s (кінцева матриця). Потім перейдіть через початкову матрицю, щоб побачити, чи потрапите ви 0. Коли ви натиснете на розташування (x, y), перейдіть до кінцевої матриці та заповніть рядок x 0s та стовпець y 0s. В кінці прочитайте остаточну матрицю, якщо значення дорівнює -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;
}

Ви не даєте жодних пояснень чи контексту для опублікованого вами коду.
Аарон

Сподіваюсь, що зараз краще. Дякуємо за попередження Я хотів би пояснити більше, якщо вам це не зрозуміло.
користувач3743369

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.