-
[KOITP] 출근자료구조 및 알고리즘/문제풀이 2017. 1. 18. 15:12
출 처 : http://koitp.org/problem/RIGHTDOWN/read/
시간 제한 메모리 제한 제출 횟수 정답 횟수 (비율) 정답자 수 1.0 초 512 MB 689 175 (25%) 160 < Comment >
해당 문제를 처음 보고 그냥 for 루프를 돌려봤었습니다. 그러나 당연하게도 시간 복잡도가 O(WHN)이 되어 시간에 맞추어 들어올 수 없었습니다. 사실 결과적으로 구하고자 하는것은 N번째 날의 출근 길이기 때문에, 길이 1번 방문할 때마다 Toggle 된다는 것에 주목하여 문제를 풀 수 있었습니다. 그 뒤로 BFS로도 구현했었는데, 속도가 느려서 오히려 그냥 3중 for 루프보다 느렸고, 따져보니 BFS를 이용하지 않아도 되었기 때문에 2중 for 루프를 이용하였습니다.
문제에서 중요한 핵심은, 과연 이 지점을 몇 번이나 방문했는가 입니다. N 번째 날의 출근길은 N-1번째 날까지 삼성이가 지나갔을 것입니다. 일단 그렇기 때문에 시작 지점인 1, 1 지점(문제 풀때는 0, 0으로 놓고 풀었습니다) 에 N-1 을 넣어두고, 분배를 시작합니다. 어차피 오른쪽 혹은 아래로만 이동할 수 있기 때문에, 1, 1 지점에서 출발할 경우, 가는 방향이 토글되면서 결국 N-1번째 날까지, 우측으로 (N-1)/2 개 아래측으로 (N-1)/2 번 들어가게 됩니다. 물론 N-1값이 홀수인 경우에는 초기값이 0인지 1인지에 따라서 분배가 달라지지만 기본적인 사상은 이렇습니다.
이를 0 ~ W, 0 ~ H 동안 루프를 돌면서 분배해 줍니다. 오른쪽 또는 아래쪽 이동이기 때문에, 위에서부터 차례대로 읽어도 무방합니다. 만약 여기서 분배되는 숫자가 0이라고 하면 그 루프는 종료해줍니다. 이렇게 되면, 각 지점을 방문한 횟수를 구하는데 O(WH)라는 시간밖에는 걸리지 않게 됩니다. 이후로는 그저 처음의 지점 상태로부터 짝수번 방문한 경우는 그대로, 홀수번 방문한 경우는 토글된 상태로 간다고 가정하여, 결과적으로 도착하는 지점만 구해주면 됩니다.
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayDeque; import java.util.Queue; import java.util.StringTokenizer; public class Commute { public static int H = 0; public static int W = 0; public static int N = 0; public static int map[][] = null; public static int count[][] = null; public static boolean isVisited[][] = null; public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); StringTokenizer st = new StringTokenizer(br.readLine()); H = Integer.parseInt(st.nextToken()); W = Integer.parseInt(st.nextToken()); N = Integer.parseInt(st.nextToken()); map = new int[H + 1][W + 1]; count = new int[H + 1][W + 1]; isVisited = new boolean[H + 1][W + 1]; for (int i = 0 ; i < H ; i++) { st = new StringTokenizer(br.readLine()); for (int j = 0 ; j < W ; j++) { map[i][j] = Integer.parseInt(st.nextToken()); } } count[0][0] = N - 1; for (int i = 0 ; i < H ; i++) { for (int j = 0 ; j < W ; j++) { if(count[i][j] == 0) { continue; } int m = count[i][j] / 2; int bigger = 0; int smaller = 0; if (count[i][j] % 2 == 0) { bigger = m; smaller = m; } else { bigger = m + 1; smaller = m; } if(map[i][j] == 0) { count[i + 1][j] += bigger; count[i][j + 1] += smaller; } else { count[i + 1][j] += smaller; count[i][j + 1] += bigger; } } } int i = 0; int j = 0; while (i != H && j != W) { if(count[i][j] % 2 == 0) { // 짝수번 방문한 경우 : 그대로 if(map[i][j] == 0) { i++; } else { j++; } } else { if(map[i][j] == 0) { j++; } else { i++; } } } bw.write("" + (i + 1) + " " + (j + 1)); bw.flush(); bw.close(); } }