Algorithm

[백준] 1074번 Z

robinjoon98 2021. 9. 25. 16:49

https://www.acmicpc.net/problem/1074

 

1074번: Z

한수는 크기가 2N × 2N인 2차원 배열을 Z모양으로 탐색하려고 한다. 예를 들어, 2×2배열을 왼쪽 위칸, 오른쪽 위칸, 왼쪽 아래칸, 오른쪽 아래칸 순서대로 방문하면 Z모양이다. N > 1인 경우, 배열을

www.acmicpc.net

문제

한수는 크기가 2N × 2N인 2차원 배열을 Z모양으로 탐색하려고 한다. 예를 들어, 2×2배열을 왼쪽 위칸, 오른쪽 위칸, 왼쪽 아래칸, 오른쪽 아래칸 순서대로 방문하면 Z모양이다.

N > 1인 경우, 배열을 크기가 2N-1 × 2N-1로 4등분 한 후에 재귀적으로 순서대로 방문한다.

다음 예는 22 × 22 크기의 배열을 방문한 순서이다.

N이 주어졌을 때, r행 c열을 몇 번째로 방문하는지 출력하는 프로그램을 작성하시오.

다음은 N=3일 때의 예이다.

풀이

이 문제는 딱 봐도 재귀적인 문제고, 분할정복 문제이다. 2차원 배열을 좌표평면처럼 생각하면, 각각이 몇 사분면에 속하는지에 따라 탐색순서가 결정됨을 알 수 있다.

N = 1인 경우를 보면

1사분면일 때 0, 2사분면일때 1, 3사분면일때 2, 4사분면일때 3임을 알 수 있다. 

N = 2인 경우를 보면

각 사분면에 해당되는 칸이 4개씩 존재한다. 또한, 그 4개의 칸을 좌표평면으로 보고 4개의 칸이 또 몇 사분면에 위치하는지에 따라 순서가 결정된다.

예컨데 1행 1열은 순서대로 1사분면, 4사분면이라 (1-1)*4 + (4-1) = 3이 되고, 3행 2열의 경우 4사분면, 3사분면이라 (4-1) * 4 + (3-1) = 14가 된다. 즉, r 행 c열이 순서대로 몇사분면에 해당되는지 적어둔 수열을 A라 하면 다음 수식이 성립한다.

즉, 주어진 n, r, c 을 사분면정보가 담긴 배열로 변환하면, O(n) 안에 문제를 풀 수 있다. 이때 N이 최대 15이므로, 사실상 상수시간에 문제를 풀 수 있게 된다.

여기까지 하고 남은건 사분면정보를구하는 일뿐인데 사분면정보를 구하는것이 생각보다 좀 까다로웠다. 우선, 좌표평면으로 볼 영역의 양 끝 지점을 알아야 한다. n, r, c rk 3, 5, 3 으로 입력된 경우를 예로들면

이 그림에서 노란색 원은 첫번째 사분면 정보를 구할때 사용되는 좌 우측 끝을 표시한 것이고, 노란색 사각형은 이때의 사분면 판단기준이 되는 기준점을 표시한 것이다. 초록색 원은 두번째 사분면 정보를 구할 때 사용되는 좌 우측끝을 표시한 것이고 초록색 사각형은 이때의 기준점을 표시한 것이다. 파란색 원은 r행 c열 을 표시한 것이다. 

사분면 정보가 무었인지에 따라, 다음번 사분면정보를 구할때 사용되는 기준점을 정하는 방법이 달라진다. 위 예시에서 첫번째 사분면 정보가 2 사분면 일 때, 죄측 끝의 열은 이전 사분면기준점의 열과 같고, 행은 이전 사분면기준점의 행 - (이전 사분면의 각 사분면의 행길이)가 된다. 우측끝은  좌측끝이 결정되었고, 길이가 정해지니 자동으로 구해진다. 

이와같이 이전의 사분면정보가 무었인지에 따라 다음 사분면기준점을 정하는 방법 4가지를 모두 구했고, 이를 코드로 옮겨 보면 아래와 같다.

import java.util.Scanner;

public class Main {
	public static void main(String args[]) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int r = sc.nextInt();
		int c = sc.nextInt();
		int[] positions = new int[n];
		int nback = n;
		positions= getPosition(n, r, c);

		int ret = 0;
		for(int i=0;i<nback;i++) {
			ret = ret + (int)Math.pow(4, nback-i-1) * (positions[i]-1);
		}
		System.out.println(ret);
		
	}
	public static int[] getPosition(int n, int r, int c) {
		int[] position = new int[n];
		int leftR=0,leftC=0;
		int rightR=(int)Math.pow(2,n)-1,rightC=rightR;
		int piviotR = (leftR+rightR)/2 + 1;
		int piviotC = (leftC+rightC)/2 + 1;
		for(int i=n;i>=1;i--) {
			if(r<piviotR && c<piviotC) {
				position[n-i]=1;
				rightR = piviotR-1;
				rightC = piviotC-1;
			}else if(r<piviotR && c>=piviotC) {
				position[n-i]=2;
				leftC = piviotC;
				leftR = piviotR - (int)Math.pow(2, i-1);
				rightR = leftR + (int)Math.pow(2, i-1)-1;
				rightC = leftC + (int)Math.pow(2, i-1)-1;
			}else if(r>=piviotR && c<piviotC) {
				position[n-i]=3;
				leftR = piviotR;
				leftC = piviotC - (int)Math.pow(2, i-1);
				rightR = leftR + (int)Math.pow(2, i-1)-1;
				rightC = leftC + (int)Math.pow(2, i-1)-1;
			}else {
				position[n-i]=4;
				leftR = piviotR;
				leftC = piviotC;
			}
			piviotR = (leftR+rightR)/2 + 1;
			piviotC = (leftC+rightC)/2 + 1;
		}
		return position;
	}
}

'Algorithm' 카테고리의 다른 글

[프로그래머스] 오픈채팅방  (0) 2021.08.30
[프로그래머스] 다단계 칫솔 판매  (0) 2021.08.25
[CodeGround] Practice - 미궁 속의 방  (0) 2021.07.28
[SCPC 2021] 친구들  (0) 2021.07.26
백준 2579 계단 오르기  (0) 2021.07.26