その行または列に0が含まれている場合、行列のすべてのセルを0に設定します。


152

0と1を持つNxN行列が与えられます。含まれているすべての行を設定し0、すべてに0Sをし、含まれているすべての列セット0すべてに0秒。

例えば

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

マイクロソフトエンジニアは、余分なメモリを必要とせず、ブール変数を2つとパスを1つだけ含むソリューションがあると私に言ったので、その答えを探しています。

ところで、それがビットマトリックスであることを想像してください。したがって、1と0だけがマトリックスに入れられます。


1
えっ?「出会った時」とは?マトリックスの要素にどのような順序で遭遇していますか?そして、すべてのビットに遭遇した場合、とにかくすべて0を取得しませんか?
ShreevatsaR

さて、要素に遭遇することを決定する順序はあなたの決定です、重要なことは、適切な要素にのみ0を設定する必要があるということです。すべてのビットが0に設定されている場合でも、はい、マトリックスはゼロで埋められます。
jaircazarin-old-account

「適切な要素」とは何ですか?「ソース」マトリックスと「ターゲット」マトリックスの2つのマトリックスが与えられ、「ターゲット」マトリックスを取得するために要素を「遭遇」する順序を決定する必要がありますか?
ShreevatsaR

1
私はあなたが「1パス」の考えのために何かを誤って聞いたと思います。それは2パスで直線的に行うことができますが、追加のメモリはなく、ブール値は2つだけです;-)したがって、私はそれが彼が意図した解決策であると強く想定しています(以下を参照)
Piotr Lesnicki

1
問題の説明が本当に正しいかどうか、友人に再確認してください。これはハミングコードまたはパリティビットで実行できると思っていましたが、これまでのところ成功していません。:)
csl

回答:


96

さて、ここでは午前3時なので疲れますが、マトリックスの各数値を2パスで最初に試してみるので、O(NxN)で、マトリックスのサイズは線形です。

1番目の列と最初の行をマーカーとして使用して、1のみの行/列がどこにあるかを確認します。次に、最初の行/列がすべて1かどうかを覚えておくための2つの変数lとcがあります。したがって、最初のパスはマーカーを設定し、残りを0にリセットします。

2番目のパスは、列と列が1とマークされている場所に1を設定し、lとcに応じて最初の行/列をリセットします。

最初の正方形は最後の正方形に依存しているので、1パスでできることを強く疑います。たぶん私の2ndパスをもっと効率的にすることができます...

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)

ここでの問題の1つは、n> sizeof(c)の場合、故障することです。これをnの一般的なケースで機能するように拡張するには、ビットフィールドを動的にサイズ設定する必要があります。これは、問題によって与えられる制約に違反すると思います。
アダム

いいえ、cはビットフィールドではなく、単なるブール値です。&=はビット単位の演算ではありません(そうですが、1ビットの値です)。cは、最初の列がすべて1(true)であるか0(false)であるかを通知するため、そこにあります。
スティーブジェソップ

2
最初の行が[0,1,1,1 ...]の場合、失敗します。私のバグ修正は、lを1ではなくm [0] [0]に初期化することです
paperhorse

確かにl = 1 for i in range(1、N):l&= m [0] [i]はl = 1 for i for range(N):l&= m [0] [i]
Kristof Neirynck

1
ところで、私は2番目のパスの条件は次のようになるはずであると考えています:if m [i] [0] | m [0] [j]:
jaircazarin-old-account

16

単一のビットは、順序付けの前後のビットに影響を与えるため、これは1つのパスでは実行できません。IOW配列をトラバースする順序に関係なく、後で0に遭遇する可能性があります。つまり、戻って前の1を0に変更する必要があります。

更新

人々は、Nをある固定値(たとえば8)に制限することで、これを1つのパスで解決できると考えているようです。まあそれはa)ポイントが欠けていることとb)元の質問ではないことです。私は並べ替えに関する質問を投稿せず、「8つのものだけを並べ替えたいと思っていると仮定して...」という答えが出るのを期待します。

とは言っても、Nが実際には8に制限されていることがわかっている場合は、これは妥当なアプローチです。上記の私の答えは、そのような制限のない元の質問に答えます。


追加のメモリがなければ、1つのパスでそれを行うことはできません。結果を格納する別のNxNマトリックスがある場合は、1つのパスで実行できます。同様に、いくつかのビット回転と2つのパスを使用すると、追加のメモリなしで実行できます。
paxos1977 2008

2
仮の行列を使用しても、1回のパスでそれを実行することはできません。それ以外の場合は、ここでは取得できない奇妙な点があります。行/列情報を推定するには1つのパス、すべてを設定するには1つのパスが必要です。
Lasse V. Karlsen、

この問題は、行ごとに1つのゼロ以外の一意の値が存在する可能性があることを認識し、参照によって割り当てることで解決しました。
Daniel Papasian 08

@ ceretullis、lassevk:まだ1つのパスではできないと思います。その2番目のマトリックスを超えるパスはカウントする必要があります。そうでない場合は、マトリックスを1つのパスでコピーし、希望どおりにコピーを操作できます。@Daniel Papasian:あなたのソリューションはN> #bits int / long / whateverでスケーリングされません
Draemon

ドラえもん、テクニックはうまくスケーリングし、それは単なる数学です。それを行うハードウェアを構築するか、ソフトウェアテクニックを使用して、単語サイズよりも大きい数値を操作できます。どちらも問題の制約に違反していない、
IMHO

10

したがって、私の考えは、最後の行/列の値をフラグとして使用して、対応する列/行のすべての値が1かどうかを示すことです。

ジグザグスキャンの使用最終行/列を除く全体マトリックスを介し。各要素で、最後の行/列の値を、それ自体と現在の要素の値との論理ANDとして設定します。つまり、0を押すと、最後の行/列は0に設定されます。1の場合、最後の行/列の値は、既に1の場合にのみ1になります。いずれの場合も、現在の要素を0に設定します。

完了したら、対応する列/行が1で埋められていれば、最終的な行/列は1になります。

最後の行と列を線形スキャンして1を探します。最後の行と列がどちらも1である行列の本体の対応する要素に1を設定します。

コーディングは、off-by-oneエラーなどを回避するのに注意が必要ですが、1つのパスで機​​能するはずです。


とても良かった...同じ行で考えていたが、最後の行/列を使用してその情報を保存できなかったので、Nx1配列のペア用に余分なメモリが足りなかった。
Dave Sherohman、2008

1
それは私に2つのパスのように見えます-1つのパスはジグザグスキャンで、2番目は「最後の行と列が両方とも1であるマトリックスの本体の対応する要素に1を設定する」です。
Adam Rosenfield、

ジグザグスキャン(誰かが私に指摘したように厳密に必要というわけではありません)はすべての行を横断しますが、最後の行/列を横断します。したがって、最終/行の列をスキャンしても、以前にスキャンした要素は複製されません。したがって、1つのパス。つまり、N * N行列の場合はO(N ^ 2)です。
Alastair

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
素晴らしい解決策ですが、技術的には、許可されている2つのブール値よりも多くのメモリを使用しています(スタック上では)。
csl

1
これは> 1パスです。(rowIndex、i)および(i、colIndex)をcheckRowおよびcheckColでアクセスされるときに印刷すると、各要素が複数回アクセスされることがわかります。
ドラえもん08

ドラえもん:あなたは正しいです。謎解きメーカーによる「シングルパス」の明確な定義が必要だと思います。彼が実際に各要素にアクセスできるのは一度だけであることを意味する場合、異なるソリューションが必要です。
アダム

元の問題(電話ゲームを介して来たもの)は、問題を「その場で」解決する必要があり、マトリックスの別のコピーがないことを意味すると思います。さらに最適なソリューションでは、処理用のストレージのような一時的なswap()も必要ありません。
アダム

また、制約が結果のマシンコードを参照していることにも疑いがある。つまり、私が提供した「コード」は2つのブール値のみを使用します。コンパイラーが行う最適化によっては、全体がインライン化されたり、他に何を知っているのかが異なります。私の解決策は正しいと思います;)
Adam

4

それは実行可能だとは思わない。最初の正方形にいて、その値が1の場合、同じ行と列の他の正方形の値が何であるかを知る方法はありません。したがって、それらをチェックし、ゼロがある場合は、最初の正方形に戻って、その値をゼロに変更する必要があります。2つのパスで実行することをお勧めします。最初のパスは、どの行と列をゼロにする必要があるかに関する情報を収集します(情報は配列に格納されるため、追加のメモリを使用しています)。2番目のパスは値を変更します。それがあなたが探している解決策ではないことは知っていますが、それは実用的な解決策だと思います。あなたが与えた制約は問題を解決不可能にします。


追加のアレイなしで、ほぼ同じソリューション(以下を参照)があります。そして、それは線形時間です(しかし、2パスします)
ピョートルレスニッキ2008

@Piotr:はい、2回目のパスは避けられないようです。私が提案した行と列の情報を格納する配列の導入により、チェックと値の変更が少なくなるため、アルゴリズムがより簡単になり、少し速くなります。これは、ストレージと効率の間のトレードオフです。
ボヤン

3

2つの整数変数と2つのパス(最大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になります。基本的にすべて1から始め、0を見つけたら行/列のビットをクリアします。CompleteColsにはビット2と3が設定されており、CompleteRowsにはビット2と4が設定されています(0ベース)。
Eclipse

次に、CompleteColsとCompleteRowsの両方のビットに対応するマトリックスのビットを設定します。
Eclipse

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 .

aとbに保存されたiとjの値のすべての値を0として印刷します。残りの値は1です。つまり、(3,3)(3,4)(5,3)と(5,4)


1

2つのパスを使用する別のソリューションは、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つのブール値を2ビット数として使用して、そのようなアルゴリズムを設計できると思いましたが、まだ成功していません。

エンジニアに問題の説明を再確認してお知らせください。確かに解決策あれば、私はその問題を回避したいと思います。


1

単一の変数を保持して、AND結合されたすべての行が何であるかを追跡します。

行が-1(すべて1)の場合、次の行をその変数への参照にします

行がそれ以外の場合、それは0です。1つのパスですべてを実行できます。疑似コード:

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

シングルパスでそれを行う必要があります-ただし、NはCPUが単一の行で演算を実行するのに十分小さいという前提があります。それ以外の場合は、各行をループしてすべてかどうかを判断する必要があります。 1秒かどうかと思います。ただし、アルゴについて質問し、ハードウェアに制約を課さない場合は、「Nビット演算をサポートするCPUを構築する...」から始めます。

Cでそれを行う方法の1つの例を次に示します。注:一緒に取得した値と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 / whateverのビット数よりも大きい場合、これは機能しないので、数えられないと思います。
ドラえもん08

また、0が配列の最下部にある場合も、値をキャッチしません(values [] = {-1、-9、-1、14、-10}で試してください)。
Eclipse

ドラえもん、私の回答の中で、質問の一部としてハードウェアの制約がなければ、「Nビット演算をサポートするCPUを構築する」から始めることを指定しています。
Daniel Papasian 2008

ジョシュ、私はついていません。CまたはPHPソリューションと提案された配列のいずれかを使用すると、6 0 6 0 0が得られます。これが正しい答えだと思います。
Daniel Papasian 2008

@Daniel-Nは定数ではないため、できません。1Mビット・ワードを使用して新しいコンピュータを構築する」のほかには、ほとんどの合理的なアルゴリズムのステップではありません。
ドラえもん

1

素敵な挑戦。このソリューションでは、スタック上に作成されたブール値を2つだけ使用しますが、関数は再帰的であるため、ブール値はスタック上に数回作成されます。

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

等々


スタックで2つ以上のブール値を使用しているため、これは正しくないと思います。
csl

悲しいことに、2つのブール値を並べ替えます。これは彼が提供した仕様に私が考えることができる最も近いものです。ここで実際の解決策を見たいです。それが実行可能な場合。
eaanon01 2008

私は、要件がスタックを増やすことについて言及しているとは思いません。これは完全に有効なソリューションだと思います。
アダム

それも私の考えです。しかし、他の誰かがより良い解決策を投稿するまで、私は確信が持てません。少なくとも私のソリューションはコンパイル可能であり、誰でも検証できます。:) ...実用的な問題のためのpsudoコードを見つけることはできません。Thnx
eaanon01 2008

1

さて、これはそのソリューションです

  • 作業用ストレージに余分なlong値を1つだけ使用します。
  • 再帰を使用しません。
  • N * Nでなく、Nのみの1つのパス。
  • Nの他の値でも機能しますが、新しい#defineが必要になります。
#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;
    }

それは確かに素晴らしい解決策です。ここでのすべてのソリューションは、少なくとも1つの要件を無視していると思います。したがって、Nの最大許容値を持つソリューションを持つことは、世界で最悪のことではありません。この問題でうまく
Adam

Nを8に制限し、これが1パスの要件を満たしていると主張するのは、まったくばかげています。これは一般的な解決策ではありません。質問にはNのサイズの制限は記載されていなかったため、副問題のみを解決しました。
ドラえもん2008

しかし、これらのソリューションはすべて、何らかの方法でNに制限があります。
AnthonyLambert 2008

Nの1パスであると言うのは明らかに完全に間違っています。元の行列の各位置の値を読み取るだけでもO(N ^ 2)であり、解を計算できるようにするには、各位置の値を少なくとも1回読み取る必要があります。値をlong long内の単一ビットとして格納する場合でも、O(N ^ 2)ビットがあるため、各ビットへのアクセスはO(N ^ 2)になります。
Alderath、2012

これは明らかに、RowGrid値がグリッド全体を格納する1つのパスであり、オプティマイザが適切であれば、最初の読み取り後、アルゴリズム全体のプロセッサレジスタの1つになります。
AnthonyLambert 2012

1

実は。アルゴリズムを実行して結果を出力するだけの場合(つまり、結果を復元しない場合)、これは1回のパスで簡単に実行できます。アルゴリズムの実行中に配列を変更しようとすると、問題が発生します。

これが私の解決策ですこれは、ギブイン(i、j)の要素の行/列の値のAND演算とそれの出力を含みます。

#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#で解決しようとしました。

私は、実際の行列とその次元を表すn以外の2つのループ変数(iとj)を使用しました

私が試したロジックは:

  1. 行列の各同心正方形に含まれる行と列のANDを計算します
  2. 角のセルに保存します(反時計回りに保存しました)。
  3. 2つのブール変数は、特定の正方形を評価するときに2つのコーナーの値を保持するために使用されます。
  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

1つのパス-私は入力を1回だけトラバースしましたが、新しい配列と2つの追加のブール変数のみを使用しました。

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に設定します。

これは追加のメモリを必要とせず、ブール変数を1つだけ使用します。残念ながら、「遠方」のエッジがすべて0に設定されている場合、それは最悪のケースであり、配列全体を2回ウォークします。


私は間違っている可能性がありますが、これで問題ありませんか?たとえば、3番目の列を実行しているときに、最初の行を処理する前に、最初の行の一番上の値が1であったか0であったかをどのようにして知ることができますか?
スティーブジェソップ

あなたは知りませんが、あなたも知る必要はありません。0の場合、列全体を0にする必要があります。前の行の値が1の場合、その上のすべての行が1であることがわかります(常にそうでした)。
Dave Sherohman、2008

0

結果行列を作成し、すべての値を1に設定します。0が検出されるとすぐにデータ行列を通過し、結果行列の行列を0に設定します

最初のパスの終わりに、結果マトリックスは正しい答えを持っています。

かなりシンプルに見えます。私が見逃しているトリックはありますか?結果セットの使用は許可されていませんか?

編集:

F#関数のように見えますが、単一のパスを実行している場合でも、関数は再帰的である可能性があるため、これは少し不正です。

関数型プログラミングを知っているかどうか、インタビュアーが理解しようとしているようです。


1
結果セットを使用すると、余分なメモリが必要になります。
cdeszaq 2008

関数型プログラミングは、元の配列を変更しません。
Svante

0

まあ、私は4つのブール値と2つのループカウンターを使用するシングルパスのインプレース(非再帰的)ソリューションを思い付きました。私はそれを2つのブール値と2つの整数に減らすことができませんでしたが、それが可能であったとしても驚かないでしょう。各セルで約3回の読み取りと3回の書き込みが行われ、O(N ^ 2)、つまり 配列サイズで線形。

これを理解するのに数時間かかった-面接のプレッシャーの下でそれを思い付く必要はありません!ブーブーを作ったら疲れすぎて見つけられない...

ええと...私は、「シングルパス」を、各値に一度触れるのではなく、マトリックスを1回スイープすることとして定義することを選択します!:-)

#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)で要素を取得でき、行列の1行と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
*/

唯一の問題は、これを機能させるために2つの追加のデータ配列を使用していることかもしれません。規定の1つは、余分なメモリを使用しないことです。でもいいね!これは基本的に私が私の答えでやったことです:)
ケニーケイソン

0

1パス、2ブール値。繰り返しの整数インデックスはカウントされないと仮定する必要があります。

これは完全な解決策ではありませんが、私はこの点に合格することができません。

0が元の0であるか、0に変換された1であるかのみを判断できれば、-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

次のようにして、1つのパスを使用できますが、入力と出力のマトリックスを使用できます。

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

ここでcol(xy)、ポイントを含む列のビットxyです。row(xy)ポイントを含む行のビットxyです。n行列のサイズです。

次に、入力をループします。おそらくスペース効率を高めるために拡張可能ですか?


0

1つの行列スキャン、2つのブール値、再帰なし。

2回目のパスを回避するにはどうすればよいですか?2番目のパスは、行または列の末尾にゼロが表示されたときにそれらをクリアするために必要です。

しかし、この問題は解決できます。行#iをスキャンするとき、行#i-1の行ステータスがわかっているためです。したがって、行#iをスキャンしているときに、必要に応じて行#i-1を同時にクリアできます。

同じソリューションが列でも機能しますが、次の反復でデータが変更されない限り、行と列を同時にスキャンする必要があります。

アルゴリズムの主要部分の実行中に値が変更されるため、最初の行と最初の列のステータスを格納するには2つのブール値が必要です。

ブール値の追加を回避するために、行列の最初の行と列の行と列に「クリア」フラグを格納しています。

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

確かに、あなたは正しいです。2つの値を格納する代わりに、3番目の可能性、たとえば-1を使用することもできます。これは、「古い」マトリックスで1であるセルを表し、最終的に0に置き換えられます。もちろん、そのようにして、乗算後の値。最後に、すべての-1が0に置き換えられます。
DFF 2013

0

これは、C ++のさまざまなNでテストされており、1
パス2つのブール再帰なし追加メモリなし ARBITRARLY LARGE N成り立ちます
(ここではソリューションのこれまでのところ、どれもこれらのすべてを行います。)

具体的には、2つのループカウンターが問題ないことをおもしろくしています。私は2つのconst unsignedを持っています。これは、読みやすさのために毎回計算されるのではなく、存在するだけです。外側のループの間隔は[0、N]で、内側のループの間隔は[1、n-1]です。switchステートメントはループ内にあり、ほとんどの場合、実際には1つのパスであることを非常に明確に示しています。

アルゴリズム戦略:

最初のトリックは、行列の内容を累積するために行列自体から行と列を作成することです。このメモリは、最初の行と列から本当に必要なすべてを2つのブール値にオフロードすることで利用可能になります。2番目のトリックは、サブマトリックスとそのインデックスの対称性を使用して、1つのパスから2つのパスを取得することです。

アルゴリズムの概要:

  • 最初の行をスキャンしてすべてがブール値である場合は格納します。最初の列も同じようにして、結果を2番目のブール値に格納します。
  • 最初の行と最初の列を除くサブマトリックスの場合:段落を読むように、左から右、上から下に反復します。各要素にアクセスしたら、逆にサブマトリックスにアクセスした場合にアクセスされるであろう対応する要素にもアクセスします。訪問した各要素について、その行が最初の列と交差する場所にその値をANDし、その列が最初の行と交差する場所にその値をANDします。
  • サブマトリックスの中心に到達したら、上記のように2つの要素を同時に訪問し続けます。ただし、訪問した要素の値を、その行が最初の列と交差する場所と、その列が最初の行と交差する場所のANDに設定します。この後、サブマトリックスが完成します。
  • 物乞いで計算された2つのブール変数を使用して、最初の行と最初の列を正しい値に設定します。

テンプレート化された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

OK、完全に一致しているわけではないことはわかっていますが、2つのブール値ではなくブール値とバイトを使用して1回のパスで取得しました。また、その効率を保証するつもりはありませんが、これらのタイプの質問では、最適ではないソリューションが必要になることがよくあります。

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

ランダムアクセスの順序でマトリックスにアクセスすることを数えない場合は、1回のパスでそれを行うことができます。これにより、最初に単一パスで行うことの利点がなくなります(キャッシュコヒーレンス/メモリ帯域幅)。

[編集:単純ですが、間違ったソリューションは削除されました]

2つのパスで実行することにより、シングルパスメソッドよりも優れたパフォーマンスが得られます。1つは行/列情報を蓄積するため、もう1つは適用するためです。配列(行優先順)は一貫してアクセスされます。キャッシュサイズを超える配列(ただし、行はキャッシュに収まる)の場合、データをメモリから2回読み取り、1回保存する必要があります。

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にヒットするかどうかを確認します。location(x、y)に到達したら、最後の行列に移動して、行xに0を、列yに0を入力します。最後に最後の行列を読み、値が-1(元の値)の場合、最初の行列と同じ場所にある値をfinalにコピーします。

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 2017年

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("");
    }

}

}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.