0と1のNxN行列が与えられた場合。 0
を含むすべての行をすべて0
sに設定し、0
を含むすべての列をすべて0
sに設定します。
例えば
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だけを行列に入れることができます。
わかりましたので、午前3時なので疲れましたが、マトリックスの各数値で正確に2回のパスで最初にインプレースを試してみるので、O(NxN)で線形ですマトリックスのサイズ。
最初の列と最初の行をマーカーとして使用して、1だけの行/列がどこにあるかを確認します。次に、最初の行/列がすべて1であるかどうかを記憶する2つの変数lとcがあります。したがって、最初のパスはマーカーを設定し、残りを0にリセットします。
2番目のパスは、行と列が1にマークされている場所に1を設定し、lとcに応じて1行目/列をリセットします。
最初の正方形は最後の正方形に依存するため、1パスでできることを強く疑います。 2回目のパスをもっと効率的にできるかもしれません...
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回のパスで行うことはできません。 IOW配列を走査する順序に関係なく、後で0に遭遇する可能性があります。つまり、前に戻って以前の1を0に変更する必要があります。
更新
Nをある固定値(たとえば8)に制限することで、これを1つのパスで解決できると考えているようです。それは、a)ポイントが欠けていること、b)元の質問ではないことです。並べ替えに関する質問は投稿せず、「8個のアイテムのみを並べ替えたいと仮定して...」という回答を期待します。
つまり、Nが実際には8に制限されていることがわかっている場合、これは合理的なアプローチです。上記の私の答えは、そのような制限のない元の質問に答えます。
したがって、私の考えは、最後の行/列の値をフラグとして使用して、対応する列/行の値がすべて1であるかどうかを示すことです。
Zig Zag scan を使用して、最終行/列を除く行列全体を使用します。各要素で、現在の要素の値との論理ANDに関して、最終行/列の値を設定します。つまり、0を押すと、最終行/列は0に設定されます。1にすると、最終行/列の値は、すでに1だった場合にのみ1になります。いずれにしても、現在の要素を0に設定します。
終了したら、対応する列/行が1で埋められている場合、最終行/列は1になります。
最終的な行と列を線形スキャンし、1を探します。最後の行と列が両方とも1であるマトリックスの本体の対応する要素に1を設定します。
オフバイワンエラーなどを回避するのは難しいですが、1回のパスで機能するはずです。
私はここに解決策を持っています、それは単一のパスで実行され、余分なメモリなしですべての処理を「インプレース」で実行します(スタックを成長させるために保存します)。
再帰を使用してゼロの書き込みを遅らせますが、これはもちろん他の行と列のマトリックスを破壊します:
#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();
}
私はそれが実行可能だとは思わない。最初の正方形にいて、その値が1の場合、同じ行と列にある他の正方形の値が何であるかを知る方法はありません。そのため、それらをチェックする必要があり、ゼロがある場合は最初の正方形に戻り、その値をゼロに変更します。 2回のパスで行うことをお勧めします-最初のパスは、どの行と列をゼロにする必要があるかに関する情報を収集します(情報は配列に格納されるため、追加のメモリを使用します)。 2番目のパスは値を変更します。私はそれがあなたが探している解決策ではないことを知っていますが、実際的な解決策だと思います。あなたが与えた制約は問題を解決不可能にします。
私は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));
問題はワンパスで解決できます
行列を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)
素敵な挑戦。このソリューションでは、スタックで作成された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
等々
さて、これは解決策です
#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;
}
C#でこの問題を解決しようとしました。
実際の行列とその次元を表すn以外に2つのループ変数(iとj)を使用しました
私が試したロジックは:
コード:
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;
}
}
}
}
単一の変数を保持して、すべての行が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);
何か不足していますか?
実際に。アルゴリズムを実行して結果を出力するだけの場合(つまり、復元しない場合)、これは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に設定します。0に遭遇するとすぐにデータ行列を調べ、結果行列の行列を0に設定します
最初のパスの終わりに、結果マトリックスに正しい答えがあります。
とてもシンプルに見えます。行方不明のトリックはありますか?結果セットを使用できませんか?
編集:
F#関数のように見えますが、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();
}
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
*/
以下のコードは、サイズm、nの行列を作成します。最初にマトリックスの次元を決定します。 matrix [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;
}
OK、私はそれが完全に一致していないことを理解していますが、2つのブールの代わりにブールとバイトを使用してワンパスで取得しました...近いです。また、私はそれの効率を保証しませんが、これらのタイプの質問は、しばしば最適なソリューションよりも少ないものを必要とします。
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; }
}
}
}
誰もバイナリ形式を使用していませんか? 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
追加のスペース要件なしで次のように動作するようです:
最初に、行の要素に要素がある行の要素を掛けると、目的の値が得られることに注意してください。
追加のスペースを使用しないようにするには(新しいマトリックスを作成してそれを埋めるのではなく、マトリックスに直接変更を適用します)、マトリックスの左上を開始し、ixiマトリックスの計算を行います(「0」で始まる) 、0))インデックス> iの要素を検討する前に。
これがうまくいくことを望みます(ハベントテステット)
私が考えることができる最も簡単な解決策を以下に貼り付けます。ロジックは、反復中にゼロに設定する行と列を記録することです。
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);
}
}
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();
}
ランダムアクセスの順序でマトリックスにアクセスすることをカウントしない場合、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;
}
}
}
入力と出力のマトリックスを使用して1つのパスを使用するには、次のようなことを実行できます。
_output(x,y) = col(xy) & row(xy) == 2^n
_
ここで、col(xy)
は、ポイントxy
を含む列のビットです。 row(xy)
は、ポイントxy
を含む行のビットです。 n
は行列のサイズです。
次に、入力をループします。おそらくスペースを効率的にするために拡張可能ですか?
さて、4つのブール値と2つのループカウンターを使用するシングルパスのインプレース(非再帰)ソリューションを思い付きました。 2つのブール値と2つのintに減らすことはできませんでしたが、可能であれば驚かないでしょう。各セルの約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;
}
これが私の解決策です。コードからわかるように、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("");
}
}
}
フラグ(ここでは制約外の数値-1を使用)を設定し、一致するすべての行と列を変更したら、すべてのフラグをゼロに置き換えます
public class Main {
public static void main(String[] args) {
//test case 1
int[][] multi = new int[][]{
{ 1, 2, 3 },
{ 4, 0, 5 },
{ 0, 6, 7 },
};
//test case 2
int[][] multi2 = new int[][]{
{ 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},
};
TwoDArraySetZero(multi2);
}
static void TwoDArraySetZero(int[][] array){
//iterate through all elements
for(int i = 0 ; i <= array.length-1 ; i++){
for (int j = 0; j <= array.length-1; j++) {
//checking if match with zero
if (array[i][j] == 0){
//set replace with -1 all matching zero row and col if not zero
for (int k = 0; k <= array.length-1 ; k++) {
if(array[i][k] != 0 )
array[i][k] = -1;
if(array[k][j] != 0)
array[k][j]= -1;
}
}
}
}
//print array
for(int i = 0; i <= array.length-1; i++)
{
for(int j = 0; j <= array.length-1; j++)
{
//replace with zero all -1
if(array[i][j] == -1)
array[i][j] = 0;
System.out.printf("%5d ", array[i][j]);
}
System.out.println();
}
}
}
これは、C++の異なるNに対して[〜#〜] tested [〜#〜]であり、次のとおりです。
ワンパス、2ボトル、再帰なし、追加メモリなし、任意の大規模Nの保留
(これまでのところ、これらのすべてを解決するソリューションはありません。)
より具体的には、2つのループカウンターは大丈夫です。私は2つのconst unsignedを持っています。これらは、読みやすくするために毎回計算されるのではなく、存在するだけです。外側のループの間隔は[0、N]で、内側のループの間隔は[1、n-1]です。 switch文は、ループ内にあることがほとんどであり、本当に1つのパスであることを非常に明確に示しています。
アルゴリズム戦略:
最初のトリックは、マトリックス自体から行と列を取得して、マトリックスの内容を蓄積することです。このメモリは、最初の行と列から知る必要のあるすべてを2つのブール値にオフロードすることで使用可能になります。 2番目のトリックは、サブマトリックスとそのインデックスの対称性を使用して、1つから2つのパスを取得することです。
アルゴリズムの概要:
Tempplatized 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;
}
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とheightを調べることで行われます-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
ここに私の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に遭遇した場合、ブール変数を設定し、その行/列を再ウォークして、エントリを0に設定します。
これは追加のメモリを必要とせず、1つのブール変数のみを使用します。残念ながら、「far」エッジがすべて0に設定されている場合、これは最悪のケースであり、アレイ全体を2回歩きます。