1辺がn枡の正方形の升目がある。各升目は黒、あるいは白で塗られている。
白い升目のみから成る、最大の正方形の一辺の長さを求めたい。
効率のよい算法を示せ。時間、空間計算量について論ぜよ。
---------------------
/*
入力の正方形は、一辺をN, 黒は0, 白は1として、(i,j)升目の色がrect[i][j]で得られるものとする。
解 時間O(N^2), 作業領域O(N),
右下角の升目座標を(i,j)とする白い正方形の最大の一辺の長さをsol(i,j) とすると、
sol(i,j) = min (sol(i-1,j), sol(i,j-1), sol(i-1,j-1)) + 1
との再帰式が成り立つ。
solをそのまま配列で作成すると、O(N^2)の作業領域を必要とする。
が、実際に必要なのは、直前の列と、sol(i,j-1)のみであることから、作業領域はO(N)で済む。
*/
class Solve0 extends Solve{
int memo[] = new int[N];
int memo_mae[] = new int[N];
void exec(){
int max = 0;
for(int j = 0; j < N; j++){
memo_mae[j] = rect[0][j];
}
for(int i = 1; i < N; i++){
memo[0] = rect[i][0];
for(int j = 1; j < N; j++){
if(rect[i][j] > 0){
memo[j] = Math.min(Math.min(memo_mae[j],memo_mae[j-1]),
memo[j-1])+1;
}
else{
memo[j] = 0;
}
/* 最大値を更新 */
if(max < memo[j]){
max = memo[j];
}
}
/*覚えておきたいものをコピー*/
for(int j = 0; j < N; j++){
memo_mae[j] = memo[j];
}
}
System.out.println("Result of Solve0:"+max);
}
}
/*
別解: 時間O(N^2), 作業領域O(N)
1.列ごとに、白い升目が連続する数を集計。
例 1 1 0 1 1 0
0 1 1 => 0 2 1
1 1 1 1 3 2
2.行ごとに1の結果を見て、max値以上の数がmax回連続すれば、そこに一辺がmax の正方形が存在する。
たとえば、上右図第3行は、2と3が連続するため、1辺が2の正方形が存在することがわかる。
プログラムは、上の1と2を同時に行うことにより、作業領域をO(N)に抑えたもの。
*/
class Solve1 extends Solve{
int memo[] = new int[N];
int memo_mae[] = new int[N];
int num = 0; /*上の2を実行する際に、連なり数をとっておく*/
int max = 0;
void exec(){
for(int i = 0; i < N; i++){
num = 0;
for(int j = 0; j < N; j++){
/* 縦に1が続く数を積算*/
if(rect[i][j] == 1){
if(i == 0)memo[j] = 1;
else
memo[j] = memo_mae[j]+1;
}
else{
memo[j] = 0;
}
/*
max+1を超える値が、max+1回続くかどうかを調べる。
もし、maxの大きさの正方形があれば、必ず前の行で
調べられているはずだから、max+1だけ調べればよい。
*/
if(memo[j] > max){
num++;
}
else{
num = 0;
}
if(num > max){
max++;
}
/*覚えておきたいものをコピー*/
memo_mae[j] = memo[j];
}
}
System.out.println("Result of Solve1:"+max);
}
}
/*
全升目をスキャンしなければならない以上、最低の計算量はO(N^2)である。
したがって、上の解の時間上の計算量は、いずれも、漸近的に最良解を与える。
*/
back