Official

H - Cを探せ! / Find C! Editorial by physics0523


上から \(i\) 行目、左から \(j\) 列目を \((i,j)\) と表現します。
また、 \(l \times m\) の領域は縦 \(l\) マス、横 \(m\) マスの領域を指します。


\(x,y,l\) を決め打ったとき、左上が \((x,y)\) である \((l+2) \times (l+2)\) の領域がレベル \(l\)C であるかどうかを効率よく判定することを考えましょう。
左上が \((x,y)\) である \((l+2) \times (l+2)\) の領域がレベル \(l\)C であることは、以下と同値です。

  • 左上が \((x+1,y+1)\) である \(l \times (l+1)\) の領域が全て . である ( # が丁度 \(0\) 個含まれる )。
  • 左上が \((x,y)\) である \((l+2) \times (l+2)\) の領域に # が丁度 \(4+3l\) 個含まれる。

この判定は、二次元累積和を用いて \(O(1)\) で解くことができます。
二次元累積和についての参考資料をいくつか挙げます。

累積和を何も考えずに書けるようにする!
2次元累積和をまなぶ
二次元での区間和を求めるアルゴリズム(区間の更新なし)
2次元累積和 | アルゴリズムビジュアル大事典

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;

//2D-Ruisekiwa Simple
int bsum[305][305]={0};

int cbz(int fx,int fy,int tx,int ty){
  if(fx>tx || fy>ty){return 0;}
  if(fx==0 && fy == 0){return bsum[tx][ty];}
  else if(fx==0){
    return bsum[tx][ty]-bsum[tx][fy-1];
  }
  else if(fy==0){
    return bsum[tx][ty]-bsum[fx-1][ty];
  }
  else{
    return bsum[tx][ty]-bsum[tx][fy-1]-bsum[fx-1][ty]+bsum[fx-1][fy-1];
  }
}

int main(){
  int n;
  cin >> n;
  for(int i=0;i<n;i++){
    string s;
    cin >> s;
    for(int j=0;j<n;j++){
      if(s[j]=='.'){bsum[i][j]=0;}
      else if(s[j]=='#'){bsum[i][j]=1;}
    }
  }

  for(int i=1;i<n;i++){
    for(int j=0;j<n;j++){
      bsum[i][j]+=bsum[i-1][j];
    }
  }
  for(int i=0;i<n;i++){
    for(int j=1;j<n;j++){
      bsum[i][j]+=bsum[i][j-1];
    }
  }

  int res=0;
  for(int i=0;i<n;i++){
    for(int j=0;j<n;j++){
      for(int l=3;i+l<=n && j+l<=n;l++){
        int w=cbz(i,j,i+l-1,j+l-1);
        int c=cbz(i+1,j+1,i+l-2,j+l-1);
        if(c!=0){continue;}
        if(w==3*(l-1)+1){res=max(res,l-2);}
      }
    }
  }
  cout << res << "\n";
  return 0;
}

posted:
last update: