- 이 풀이는 직전 포스트인 "백준 7569 '토마토'"의 풀이를 수정하는 것을 기준으로 서술하겠다. 같이 참고하면 좋다.
- 또한 7576번 (2차원 토마토), 7569번 (3차원 토마토)과 완전히 같은 논리로 11차원 배열과 11중 for문을 사용해서 문제를 풀 수도 있지만, 풀이 의의를 위해 임의의 차원 수에 대한 일반적인 코드를 짜기로 하였다.
- 아래의 코드에서
DIM = 2
로 고치면 7576번의 풀이가 되고, DIM = 3
으로 고치면 7569번의 풀이가 된다. 이 풀이는 DIM = 11
이므로 11차원인 '하이퍼 토마토'의 풀이가 된다.
아이디어
- 임의 차원(여기서는 11차원)에 대한 좌표를 다루는 것은 어렵기 때문에, 좌표를 1차원으로 평탄화(flatten)할 것이다.
- 즉, 모양이 m×n×⋯×w인 11차원 배열을 크기가 mn⋯w인 1차원 배열로 만들어 다룰 것이다.
- 이하 일반적인 설명과 코드를 위해 m=W1,n=W2,⋯,w=W11 등으로 쓰고, W0=1이라 하고, Pn=W1W2⋯Wn이라 하자.
- 이때 1차원 배열의 크기는 mn⋯w=W1W2⋯W11=P11임을 알 수 있다. 일반적으로 dim 차원을 평탄화 시킨 배열의 크기는 Pdim임도 관찰할 수 있다.
- 그렇게 할 때, i차원 방향에 대한 변위 벡터 dxi는 ±Pi−1로 대응되는 것을 볼 수 있다. (1≤i≤dim)
- dx1=(±1,0,0,⋯,0)→±1+0m+0mn+⋯+0mnopqrstuv=±1
- dx2=(0,±1,0,⋯,0)→ 0±1m+0mn+⋯+0mnopqrstuv=±m
- dx3=(0,0,±1,⋯,0)→ 0+0m±1mn+⋯+0mnopqrstuv=±mn
- ⋯
- dxi→±Pi−1
- nxi=xi+dxi의 범위 체크: i+1차원에 대한 인덱스가 달라지는지 확인하면 된다.
- (xi의 i+1차원에 대한 인덱스) = ⌊xi/dxi+1⌋ mod dxi+2=⌊xi/Pi⌋
- 구현할 때 음수 나눗셈에 주의한다.
코드
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
static BufferedReader rd = new BufferedReader(new InputStreamReader(System.in));
static StringTokenizer tk = null;
static final int DIM = 11;
static final int RIPEN = 1, UNRIPEN = 0, EMPTY = -1;
static int ans;
static int[] W = new int[DIM+1];
static int[] P = new int[DIM+1];
static int[] map;
static int cnt, tomato;
static Queue<Integer> q = new ArrayDeque<>();
static boolean[] enqueued;
public static void main(String[] args) throws Exception {
W[0] = 0;
P[0] = 1;
tk = new StringTokenizer(rd.readLine());
for (int d=1; d<=DIM; d++) {
W[d] = Integer.parseInt(tk.nextToken());
P[d] = P[d-1] * W[d];
}
map = new int[P[DIM]];
enqueued = new boolean[P[DIM]];
for (int y=0; y<P[DIM]/W[1]; y++) {
tk = new StringTokenizer(rd.readLine());
for (int x=0; x<W[1]; x++) {
int idx = y * W[1] + x;
map[idx] = Integer.parseInt(tk.nextToken());
if (map[idx] == RIPEN) {
q.offer(idx);
enqueued[idx] = true;
cnt++;
}
if (map[idx] != EMPTY)
tomato++;
}
}
while (true) {
int size = q.size();
for (int i=0; i<size; i++) {
int x = q.poll();
for (int d=1; d<=DIM; d++) {
for (int sgn=-1; sgn<=1; sgn+=2) {
int dx = sgn * P[d-1];
int nx = x + dx;
if ((P[d] + x) / P[d] != (P[d] + nx) / P[d]) continue;
if (map[nx] == EMPTY) continue;
if (enqueued[nx]) continue;
q.offer(nx);
enqueued[nx] = true;
map[nx] = RIPEN;
cnt++;
}
}
}
if (q.isEmpty()) break;
ans++;
}
if (cnt < tomato)
System.out.println(-1);
else
System.out.println(ans);
}
}
메모리 및 시간
- 메모리: 100368 KB
- 시간: 340 ms
리뷰
- "구데기성"으로 악명 높은 구데기컵의 '하이퍼 토마토' 문제를 풀어보았다.
- 다차원 자료구조를 1차원으로 평탄화시켜야 쉬워진다는 점에서 의외로 실용적인 문제였다.
- 첫 시도는 임의차원 텐서를 N진 트리를 이용해 재귀적으로 정의하는 방법을 사용했는데, 재귀 때문인지 시간초과가 났다. (코드)
- 이 코드는 7569번(3차원 토마토)는 겨우 통과했다. (358492 KB, 1336 ms)