R×C 격자(0/1)에서 1로 이루어진 마름모(다이아몬드) 경계 중 가장 큰 크기 찾기. 크기 k 마름모는 가로 폭 (2k−1), 세로 높이 (2k−1)의 속이 빈 마름모
각 칸에서 4방향(↖ ↗ ↙ ↘)으로 연속한 1의 길이를 미리 계산해두면, 마름모의 4변 검사가 O(1)로 끝남. 위 꼭짓점을 기준으로 가능한 가장 큰 k부터 내림차순 시도하고 현재 답보다 작아지면 컷
각 셀에서 대각선 방향으로 1이 몇 칸 연속하는지 미리 계산
| 변수 | 방향 | 점화식 | 순회 순서 |
|---|---|---|---|
lu | ↖ | lu[i-1][j-1] + 1 | 위→아래, 왼→오 |
ru | ↗ | ru[i-1][j+1] + 1 | 위→아래, 오→왼 |
ld | ↙ | ld[i+1][j-1] + 1 | 아래→위, 왼→오 |
rd | ↘ | rd[i+1][j+1] + 1 | 아래→위, 오→왼 |
g[i][j] == 0이면 모두 0으로 초기화
int[R+2][C+2] 로 격자를 둘러싸고 실제 데이터를 (1, 1) ~ (R, C)에 저장. 패딩 셀이 자동 0이 되어 i-1, j-1 같은 경계 참조 시 별도 분기 없이 동작
위 꼭짓점 (r, c), 크기 k의 마름모는
(r, c) ← 위 꼭짓점
↙ ↘
ld rd 변
↘ ↙
(bottomRow, c) ← 아래 꼭짓점, bottomRow = r + 2(k-1)
↖ ↗
lu ru 변
4변 모두 길이 k 이상이면 마름모 존재
ld[r][c] ≥ k && rd[r][c] ≥ klu[bottomRow][c] ≥ k && ru[bottomRow][c] ≥ k크기 k 마름모는 위 꼭짓점 → 중심까지 (k−1) 칸, 중심 → 아래 꼭짓점까지 (k−1) 칸. 합쳐서 위에서 아래까지 행 거리는 2(k−1)
k=3 예시:
row r: . ← 위 꼭짓점
row r+1: . .
row r+2: . . ← 중심
row r+3: . .
row r+4: . ← 아래 꼭짓점 (r에서 4칸 = 2(k-1))
각 (r, c)를 위 꼭짓점 후보로
maxKtop = min(ld[r][c], rd[r][c]) ← 위쪽 두 변으로 가능한 최대 k
for k = maxKtop; k > ans; k--:
bottomRow = r + 2(k-1)
if bottomRow > R: continue ← 격자 밖이면 skip
if lu[bottomRow][c] >= k && ru[bottomRow][c] >= k:
ans = k; break ← 큰 k부터 내려왔으니 첫 성공이 곧 최댓값
핵심 가지치기: k > ans 조건으로 현재 답 이하의 k는 아예 시도 안 함. 이 가지치기가 750×750 시간 제한 통과의 결정적 요인
// 4방향 대각선 누적합
for (int i = 1; i <= R; i++)
for (int j = 1; j <= C; j++)
lu[i][j] = g[i][j] == 1 ? lu[i - 1][j - 1] + 1 : 0;
for (int i = 1; i <= R; i++)
for (int j = C; j >= 1; j--)
ru[i][j] = g[i][j] == 1 ? ru[i - 1][j + 1] + 1 : 0;
for (int i = R; i >= 1; i--)
for (int j = 1; j <= C; j++)
ld[i][j] = g[i][j] == 1 ? ld[i + 1][j - 1] + 1 : 0;
for (int i = R; i >= 1; i--)
for (int j = C; j >= 1; j--)
rd[i][j] = g[i][j] == 1 ? rd[i + 1][j + 1] + 1 : 0;
int ans = 0;
for (int r = 1; r <= R; r++) {
for (int c = 1; c <= C; c++) {
int maxKtop = Math.min(ld[r][c], rd[r][c]);
for (int k = maxKtop; k > ans; k--) {
int bottomRow = r + 2 * (k - 1);
if (bottomRow > R) continue;
if (lu[bottomRow][c] >= k && ru[bottomRow][c] >= k) {
ans = k;
break;
}
}
}
}
O(R × C × min(R, C))k > ans 가지치기로 실제로는 훨씬 적음O(R × C)package rtaeho.week04.B1028;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int R = Integer.parseInt(st.nextToken());
int C = Integer.parseInt(st.nextToken());
int[][] g = new int[R + 2][C + 2];
for (int i = 1; i <= R; i++) {
String line = br.readLine();
for (int j = 1; j <= C; j++) {
g[i][j] = line.charAt(j - 1) - '0';
}
}
int[][] lu = new int[R + 2][C + 2]; // 왼쪽위
int[][] ru = new int[R + 2][C + 2]; // 오른쪽위
int[][] ld = new int[R + 2][C + 2]; // 왼쪽아래
int[][] rd = new int[R + 2][C + 2]; // 오른쪽아래
// 왼쪽위
for (int i = 1; i <= R; i++) {
for (int j = 1; j <= C; j++) {
lu[i][j] = g[i][j] == 1 ? lu[i - 1][j - 1] + 1 : 0;
}
}
// 오른쪽위
for (int i = 1; i <= R; i++) {
for (int j = C; j >= 1; j--) {
ru[i][j] = g[i][j] == 1 ? ru[i - 1][j + 1] + 1 : 0;
}
}
// 왼쪽아래
for (int i = R; i >= 1; i--) {
for (int j = 1; j <= C; j++) {
ld[i][j] = g[i][j] == 1 ? ld[i + 1][j - 1] + 1 : 0;
}
}
// 오른쪽아래
for (int i = R; i >= 1; i--) {
for (int j = C; j >= 1; j--) {
rd[i][j] = g[i][j] == 1 ? rd[i + 1][j + 1] + 1 : 0;
}
}
int ans = 0;
for (int r = 1; r <= R; r++) {
for (int c = 1; c <= C; c++) {
// 왼쪽아래와 오른쪽아래 중에 작은 값을 기준으로 진행
int maxKtop = Math.min(ld[r][c], rd[r][c]);
for (int k = maxKtop; k > ans; k--) {
int bottomRow = r + 2 * (k - 1);
if (bottomRow > R) {
continue;
}
if (lu[bottomRow][c] >= k && ru[bottomRow][c] >= k) {
ans = k;
break;
}
}
}
}
System.out.println(ans);
}
}