C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で1のみを含む正方形の部分行列を数える方法

m × n のサイズを持つバイナリ行列(0と1のみで構成される行列)が与えられたとき、すべての要素が1である正方形の部分行列の総数を数える問題を考えてみましょう。

例えば、次のような行列が与えられたとします。

0111
1111
0111

この場合、答えは 15 となります。内訳は以下の通りです。

  • 1 × 1 の正方形(1つの1からなる):10個
  • 2 × 2 の正方形(4つの1からなる):4個
  • 3 × 3 の正方形(9つの1からなる):1個

解法のアプローチ:動的計画法(DP)

この問題は、動的計画法を用いることで効率的に解くことができます。基本的な考え方は、「各セルを右下の角とする正方形の個数」をそのセルの値として記録していくというものです。

あるセル matrix[i][j] が1であるとき、そこで終わる正方形の数は、右・下・右下の隣接セルの値の最小値に1を加えたものになります。これは、より大きな正方形を作るためには、周囲の3方向すべてが同じ大きさの正方形を形成している必要があるためです。

アルゴリズムの手順

  1. 答えを格納する変数 ans を0で初期化し、行数を n、列数を m とします。
  2. 最下行の各セルの値を ans に加算します(最下行のセルは1×1の正方形としてしか使えないため)。
  3. 最右列の各セルの値を ans に加算します。
  4. この際、右下隅のセルが二重にカウントされているため、matrix[n-1][m-1] を一度差し引きます。
  5. 残りのセルについて、in-2 から 0 へ、jm-2 から 0 へと逆順に走査します。
    • matrix[i][j] == 1 の場合:matrix[i][j] = 1 + min(matrix[i+1][j+1], matrix[i][j+1], matrix[i+1][j]) と更新します。
    • それ以外の場合:matrix[i][j] = 0 とします。
    • 更新後の値を ans に加算します。
  6. 最後に ans を返します。

C++による実装例

#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
   int countSquares(vector<vector<int>>& matrix) {
      int ans = 0;
      int n = matrix.size();
      int m = matrix[0].size();
      for(int i = 0; i < m; i++)ans += matrix[n-1][i];
      for(int i = 0; i < n; i++)ans += matrix[i][m-1];
      ans -= matrix[n-1][m-1];
      for(int i = n - 2;i >= 0; i--){
         for(int j = m-2 ;j >= 0; j--){
            matrix[i][j] = matrix[i][j] == 1? 1 + min({matrix[i+1][j+1],matrix[i][j+1],matrix[i+1][j]}) : 0;
            ans += matrix[i][j];
         }
      }
      return ans;
   }
};
main(){
   vector<vector<int>> v = {{0,1,1,1},{1,1,1,1},{0,1,1,1}};
   Solution ob;
   cout << (ob.countSquares(v));
}

入力

[[0,1,1,1],
[1,1,1,1],
[0,1,1,1]]

出力

15

計算量の分析

このアルゴリズムの時間計算量は O(m × n) です。行列の各セルを一度だけ訪問するためです。また、入力行列自体をDPテーブルとして再利用しているため、追加の空間計算量は O(1) で済みます(入力を書き換えてもよい場合)。全探索的にすべての正方形候補を調べる方法では O((mn)²) 程度かかる可能性がありますが、このDP手法により大幅な高速化が実現できます。

  1. C++でA % X = BとなるXの取り得るすべての値の個数を求める

    問題概要2つの整数AとBが与えられ、「A % X = B」を満たすような整数Xの取り得る値の個数を求めるのが目標です。この条件式については、AとBの大小関係によって次のように場合分けができます。A == B の場合:Xは無限に多くの値を取り得るため、-1を返します。A < B の場合:解が1つも存在しないため、0を返します。A > B の場合:(A − B) の約数のうちBより大きいものの個数を結果として返します。考え方のポイント剰余演算の性質上、「余りは必ず割る数よりも小さくなる」ため、A % X = B が成り立つには X > B であることが必要です。また、A % X

  2. 【C++】n個の点のうちm個が同一直線上にあるときに作れる三角形の数を求める方法

    問題の概要2次元平面上の点の総数を表す2つの変数 n と m が与えられます。このうち m 個の点は同一直線上(コリニア)に並んでいます。ここでの課題は、これら n 個の点から作ることができる三角形の数を求めることです。同一直線上の点(共線点)とは、同じ一本の直線上に乗っている点のことです。例えば下図では、点 A と点 B が同一の直線上に位置しています。考え方の基本まず、n=4(A, B, C, D)、m=2(A, B)という具体例で確認してみましょう。三角形の数は次の手順で計算できます。・4 点から任意の 3 点を選ぶ組み合わせ = 4C3・ただし、同一直線上の点だけでは三角形が成立しない