아이디어
1. 셀 좌표 파싱과 매핑
- 문자열 패턴이 복잡하거나 없으면 Map을 쓰는 수밖에 없지만… 기왕 패턴이 주어졌으니 좌표를 정수로 매핑시켜 일반적인 그래프 탐색으로 만들기로 했다.
(알파벳에 해당하는 수) * 1000 + (숫자 좌표)
로 바꾼다.
- “A37” → 1037, “U238” → “21238”, “AC89” → 29089 (= (126+3)1000+89)
- R을 이용해서 더미 인덱스가 없도록 할 수는 있지만 이렇게 하는 편이 디버깅이 쉬우니깐…
2. 입력 처리
- StringTokenizer를 이용해 셀 한 개씩 입력을 받는다.
- 이때 받은 단어?를 “+”를 구분자로 해서 다시 나눈다.
- 나눠진 토큰이 2개라면 두 좌표에 해당하는 간선을 추가하고 각각 진입차수를 올린다.
- 나눠진 토큰이 1개일 때 그 문자열의 길이가 1이면 (즉 “.”이면) 무시한다. 아니라면 간선 추가하고 진입차수 올린다.
3. 정점 개수 세기
- 모든 셀을 돌며 진입/진출차수 둘 다 0이 아닌 셀만 이 문제에서 관심있는 정점이라 생각하고 개수를 세어 놓는다.
- 이후 위상 정렬 결과와 비교해야 한다.
4. 위상정렬과 결과 출력
- 수업 시간에 배운 대로…
- 위상 정렬을 완료한 후 탐색한 정점 수가 전체 정점보다 작으면 사이클이 있었다는 뜻이다.
- 사이클 여부에 따라 결과를 출력한다.
코드
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
static BufferedReader rd = new BufferedReader(new InputStreamReader(System.in));
static StringBuilder sb = new StringBuilder();
static StringTokenizer tk;
static int IDX_CNT = parseIndex("ZZ999") + 1;
static int R, C, vCnt;
static List<List<Integer>> graph = new ArrayList<>();
static int[] indeg = new int[IDX_CNT];
static Queue<Integer> q = new ArrayDeque<>();
public static void main(String[] args) throws Exception{
for (int i=0; i<IDX_CNT; i++)
graph.add(new ArrayList<>());
tk = new StringTokenizer(rd.readLine());
R = Integer.parseInt(tk.nextToken());
C = Integer.parseInt(tk.nextToken());
for (int y=1; y<=R; y++) {
tk = new StringTokenizer(rd.readLine());
for (int x=1; x<=C; x++) {
int idx = y + x * 1000;
StringTokenizer ptk = new StringTokenizer(tk.nextToken(), "+");
if (ptk.countTokens() == 2) {
int prcdIdx1 = parseIndex(ptk.nextToken());
int prcdIdx2 = parseIndex(ptk.nextToken());
graph.get(idx).add(prcdIdx1);
graph.get(idx).add(prcdIdx2);
indeg[prcdIdx1]++;
indeg[prcdIdx2]++;
}
else {
String s = ptk.nextToken();
if (s.length() == 1) continue;
int prcdIdx = parseIndex(s);
graph.get(idx).add(prcdIdx);
indeg[prcdIdx]++;
}
}
}
for (int i=0; i<IDX_CNT; i++) {
int outdeg = graph.get(i).size();
if (indeg[i] > 0 || outdeg > 0)
vCnt++;
if (indeg[i] == 0 && outdeg > 0)
q.offer(i);
}
int cnt = 0;
while (!q.isEmpty()) {
int n = q.poll();
for (int m: graph.get(n)) {
indeg[m]--;
if (indeg[m] == 0) {
q.offer(m);
}
}
cnt++;
}
System.out.println((vCnt > cnt) ? "yes" : "no");
}
static int parseIndex(String s) {
int a = 0, n = 0;
int i = 0;
char[] chs = s.toCharArray();
while (chs[i] >= 'A') a = a * 26 + chs[i++] - 'A' + 1;
while (i < chs.length) n = n * 10 + chs[i++] - '0';
return a * 1000 + n;
}
}
메모리 및 시간
- 메모리: 259228 KB (259 MB)
- 시간: 1316 ms
리뷰
- 통과가 되었다는 것이 신기할 정도의 시간과 메모리
- parseIndex에서 더미 인덱스를 없애고 API가 아닌 배열로 하면 되려나