web-dev-qa-db-ja.com

動的プログラミング-最大の正方形ブロック

1と0でいっぱいの巨大なファイルで、1の最大の正方形を見つける必要があります。動的プログラミングを使用する必要があることはわかっています。 2D配列に格納しています。最大の正方形を見つけるためのアルゴリズムの助けがあれば素晴らしいです、ありがとう!

入力例:

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

回答:

1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1

これまでの私のコード:

int Square (Sq[int x][int y]) {
   if (Sq[x][y]) == 0) {
       return 0;
   }
   else {
       return 1+MIN( Sq(X-1,Y), Sq(X,Y-1), Sq(X-1,Y-1) );
   }
}

(すでに配列に値が入力されていると想定)

int main() {
    int Sq[5][6]; //5,6 = bottom right conner
    int X = Square(Sq[5][6]);
}

そこからどうすればいいですか?

40
batt

これがソリューションのスケッチです:

各セルについて、そのセルを左上として使用して正方形を作成できる大きさのカウンターを保持します。明らかに、0のすべてのセルはカウントとして0を持ちます。

右下のセルから反復を開始し、左下に移動してから、1行上に移動して繰り返します。

各スキャンでこれを行います:

  1. セルに0がある場合は、count=0を割り当てます
  2. セルに1があり、エッジセル(下または右のエッジのみ)の場合、count=1を割り当てます。
  3. 他のすべてのセルについては、そのセルの右、右下、および下のセルの数を確認します。それらの最小値を取り、1を追加して、それをカウントに割り当てます。これまでの最大数を追跡するために、グローバルmax_count変数を保持します。

行列のトラバースの最後に、max_countは目的の値になります。

複雑さは、マトリックスの走査のコストにすぎません。

これは、走査後のマトリックスの外観です。括弧内の値はカウント、つまりセルを左上として使用して作成できる最大の四角形です。

1(1) 0(0) 1(1) 0(0) 1(1) 0(0)
1(1) 0(0) 1(4) 1(3) 1(2) 1(1)
0(0) 1(1) 1(3) 1(3) 1(2) 1(1)
0(0) 0(0) 1(2) 1(2) 1(2) 1(1)
1(1) 1(1) 1(1) 1(1) 1(1) 1(1)

Pythonでの実装

def max_size(mat, ZERO=0):
    """Find the largest square of ZERO's in the matrix `mat`."""
    nrows, ncols = len(mat), (len(mat[0]) if mat else 0)
    if not (nrows and ncols): return 0 # empty matrix or rows
    counts = [[0]*ncols for _ in xrange(nrows)]
    for i in reversed(xrange(nrows)):     # for each row
        assert len(mat[i]) == ncols # matrix must be rectangular
        for j in reversed(xrange(ncols)): # for each element in the row
            if mat[i][j] != ZERO:
                counts[i][j] = (1 + min(
                    counts[i][j+1],  # east
                    counts[i+1][j],  # south
                    counts[i+1][j+1] # south-east
                    )) if i < (nrows - 1) and j < (ncols - 1) else 1 # edges
    return max(c for rows in counts for c in rows)
79
Joy Dutta

LSBRA(X,Y)は、「X、Yで右下にある最大の正方形」を意味します

疑似コード:

_LSBRA(X,Y):
   if (x,y) == 0:
       0
   else:
       1+MIN( LSBRA(X-1,Y), LSBRA(X,Y-1), LSBRA(X-1,Y-1) )
_

(エッジセルの場合、MIN部分をスキップして、(x、y)が0でない場合は1を返すことができます。)

次のように、「ウェーブ」でグリッドを斜めに通過します。

_    0 1 2 3 4
  +----------
0 | 1 2 3 4 5
1 | 2 3 4 5 6
2 | 3 4 5 6 7
3 | 4 5 6 7 8
_

または、エッジセルに入力している限り、左から右、上から下の順に作業します。

_    0 1 2 3 4
  +----------
0 | 1 2 3 4 5
1 | 6 7 8 9 .
2 | . . . . .
3 | . . . . .
_

そうすれば、必要なデータを以前に計算したことがない計算に遭遇することは決してありません。したがって、すべてのLSBRA() "calls"は、実際には以前の計算結果の単なるテーブルルックアップです(したがって動的プログラミング)側面)。

動作する理由

X、Yで右下の正方形を作成するには、他の3つのコーナーのそれぞれに接する、寸法が1つ少ない重複する正方形が含まれている必要があります。つまり、

_XXXX
XXXX
XXXX
XXXX
_

あなたも持っている必要があります...

_XXX.    .XXX    ....    ....
XXX.    .XXX    XXX.    ....
XXX.    .XXX    XXX.    ....
....    ....    XXX.    ...X
_

これら3つ(各LSBRAチェック)のNサイズの正方形に加えて現在の正方形も「占有」されている限り、(N + 1)サイズの正方形になります。

8
Amber

私の頭に浮かぶ最初のアルゴリズムは次のとおりです。

  1. '&&'列/行1と列/行2の場合、これは、各エントリと他の列/行の対応するエントリとの間で '&&'操作を実行することを意味します。
  2. 結果の列を確認します。長さが2 1である場合は、2x2の正方形に当たることを意味します。
  3. そして、次の列は最初の2つの結果です。長さ3の1がある場合、3x3の正方形をヒットしました。
  4. すべての列が使用されるまで繰り返します。
  5. 列2から1〜4を繰り返します。

実装は非常に簡単なので、問題は宿題のように聞こえません。さらに、入力が非常に大きい場合、これが遅くなるため、これを行うにははるかに効率的な方法がある可能性があります。

3
DeusAduro

入力行列をMとしましょう:n x m

T[i][j]は、正方形の右下角が最大の正方形の辺を含むDPマトリックスです(i,j)

テーブルを満たすための一般的なルール:

if (M[i][j] == 1) {
  int v = min(T[i][j-1], T[i-1][j]);
  v = min(v, T[i-1][j-1]);
  T[i][j] = v + 1;
}
else 
  T[i][j] = 0;

結果の正方形サイズはTの最大値です。

充填 T[i][0]およびT[0][j]はささいなことです。

このアルゴを巨大なファイルに使用できるかどうかはわかりませんが、-マトリックスT全体を保存する必要はありませんが、現在と以前の行のみ。

以下のメモは、一般的な考え方を理解するのに役立ちます。

  • サイズsの右下角(i-1、j)、(i、j-1)、(i-1、j-1)のすべての正方形は、サイズsの右下角(i、j)の正方形の内側にあります+1。
  • 右下隅が(i、j)のサイズs + 1の正方形がある場合、右下角(i-1、j)、(i、j-1)、(i-1、 j-1)は少なくともsです。
  • 反対も真実です。 (i-1、j)、(i、j-1)、(i-1、j-1)で右下角を持つ少なくとも1つの正方形のサイズがsより小さい場合、右下隅の正方形のサイズ(i、j)はs + 1より大きくすることはできません。
2
sergtk

ここで重要なのは、動的プログラミングを使用して、実際のエリアではなく、エリアのrootを追跡できることです。

アルゴリズムは次のとおりです。

Max-squareと呼ばれるintの2D配列を格納します。ここで、インデックスi、jの要素は、それが入っている正方形のサイズを表し、i、jは右下隅です。 (max [i、j] = 2の場合、インデックスi、jがサイズ2 ^ 2 = 4の正方形の右下隅であることを意味します)

各インデックスi、jについて:

ifi、jで要素が0の場合、max-square i、jを0に設定します。

else:

Max-square [i-1、j]とmax-square [i、j-1]とmax-square [i-のminimumを見つけます1] [j -1]。 max-square [i、j]を1 + 3の最小値に設定します。帰納的に、max-square配列を埋めることになります。プロセス内の最大値を検索または追跡し、その値を返します^ 2。

人々が提案したこれらのソリューションを見てください: https://leetcode.com/discuss/questions/oj/maximal-square?sort=votes

1
Han Sheng Huang

わかりました。最も非効率的な方法ですが、簡単な方法は次のとおりです。

  1. 最初のアイテムを選択します。 1かどうかを確認します。1の場合、1x1の正方形があります。

  2. 下の1つと右の1つをチェックし、1の場合は、行2の列2をチェックします。1の場合は、2x2の正方形です。

  3. 行3の列1、列2、列3に加えて、行1の列3、行2の列3、1の場合は3x3をチェックします。

  4. したがって、基本的には行と列を一緒に展開し続け、境界内のすべてのセルをチェックします。 0を押すとすぐに壊れるので、1ポイント続けて移動し、最初からやり直します。

  5. 行の終わりで、次の行に移動します。

  6. 終わりまで。

おそらく、それらがwhileループなどにどのように適合するか、&&sを使用して0を確認する方法を確認できます。また、これを見ると、どのように高速化できるかに気付くでしょう。しかし、他の答えが述べたように、それは宿題のように聞こえるので、実際のコードはあなたに任せます。

幸運を!

1
Mark Mayo

Nを2D配列のセルの量とします。すべての最大の空の長方形をリストする非常に効率的なアルゴリズムが存在します。最大の空の四角形はこれらの空の四角形の1つ内にあり、最大の空の四角形のリストが計算されると、それを見つけるのは簡単です。このようなリストを作成するためのO(N)アルゴリズムを提示する論文は www.ulg.ac.be/telecom/rectangles とソースコードで見つけることができます(最適化されていません。)最大の空の四角形の数がNで囲まれているという証明が存在することに注意してください(論文を参照)。したがって、最大の空の四角形の選択はO(N)で行うことができ、全体的な方法もOです。 (N)実際には、この方法は非常に高速です。コード全体がCの40行を超えてはならないため、実装は非常に簡単です(すべての最大の空の四角形をリストするアルゴリズムは、Cの約30行かかります) )。

0
S. Piérard