티스토리 뷰
Solved.ac Class 완전정복 프로젝트
Class : 3 ~ 3 ++
링크
https://www.acmicpc.net/problem/1074
1074번: Z
한수는 크기가 2N × 2N인 2차원 배열을 Z모양으로 탐색하려고 한다. 예를 들어, 2×2배열을 왼쪽 위칸, 오른쪽 위칸, 왼쪽 아래칸, 오른쪽 아래칸 순서대로 방문하면 Z모양이다. N > 1인 경우, 배열을
www.acmicpc.net
문제
문제
한수는 크기가 2N × 2N인 2차원 배열을 ZZ 모양으로 탐색하려고 한다. 예를 들어, 2 × 2 배열을 왼쪽 위칸, 오른쪽 위칸, 왼쪽 아래칸, 오른쪽 아래칸 순서대로 방문하면 Z모양이다.
N > 1인 경우, 배열을 크기가 2N-1 × 2N-1로 4등분 한 후에 재귀적으로 순서대로 방문한다.
다음 예는 22 × 22 크기의 배열을 방문한 순서이다.
N이 주어졌을 때, r행 c열을 몇 번째로 방문하는지 출력하는 프로그램을 작성하시오.
다음은 N=3일 때의 예이다.
입력
첫째 줄에 정수 N, r, c가 주어진다.
출력
r행 c열을 몇 번째로 방문했는지 출력한다.
접근방법
문제에서 주어진 제한 시간을 째려보자. 제한은 0.5초다. 2^N의 배열을 위 조건에 맞게 넘버링 후 result [r][c]를 출력하려고 한다면, 반드시 시간 초과가 날 것이다. 즉 문제는 너 배열 없이 문제를 구해볼 수 있니?라고 묻고 있다. 또한 문제의 Z형태가 반복되어 사용되고 있다. 이 점을 인지하고 풀이로 들어가자.
풀이
Sample3을 예로 보자. Sample 3의 넘버링 방식을 수기로 작성해보면 다음과 같다.
- 배열을 4 분할한다. 배열의 순서는 상단 좌측, 상단 우측 , 하단 좌측, 하단 우측 순이다.
- 나눈 배열의 크기가 1이 아니라면, 다시 1번을 반복한다.
- 나눈 배열의 크기가 1이라면 상단 좌측, 상단 우측 , 하단 좌측, 하단 우측 순으로 번호를 매긴다.
이러한 재귀적인 과정을 통해 마지막 번호까지 넘버링을 하게 된다. 이 문제를 배열 없이 해결하려면, 이러한 재귀적인 과정에서 규칙성을 찾아야 한다. 본인은 다음과 같은 규칙성을 찾아내 풀었다.
Sample3을 예제로 N=3인 경우, 처음 4 분할하고, 각 분할된 면들을 문제가 넘버링하는 순서에 따라 K사분면이라고 부르겠다.
먼저 좌표가 어디에 있는지부터 찾아보자. 좌표를 찾는 방법은 쉽다. 배열을 4 분할하여, 자신이 찾는 r행 c열이 나올 때까지, r행 c열을 포함한 구역만 재귀적으로 구하면 된다. 함수 한 번에 찾을 영역이 1/4씩 줄어든다.
그렇다면 위치는 찾았는데, 어떻게 값을 구할까?
각 사분면들은 16개의 값을 가질 수 있으므로, 각 사분면의 시작 값은 아래와 같다.
- 1 사분면의 시작 값은 첫 번째 사분면이므로 0+(1-1)*(4*4) = 0이다.
- 2 사분면의 시작 값은 두 번째 사분면이므로 0+(2-1)*(4*4) = 16이다.
- 3 사분면의 시작 값은 세 번째 사분면이므로 0+(3-1) 2*(4*4) =32이다.
- 4 사분면의 시작 값은 네 번째 사분면이므로 0+(4-1)*(4*4) = 48이다.
각 사분면마다의 식의 의미는 배열 시작 값 + (사분면 번호 -1) *(영역의 크기 )이다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
Main test = new Main();
}
public Main() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer stk = new StringTokenizer(br.readLine()," ");
int N = Integer.parseInt(stk.nextToken());
int r = Integer.parseInt(stk.nextToken());
int c = Integer.parseInt(stk.nextToken());
System.out.println(solution(N,r,c,0,0,0));
}
private int solution(int n, int r, int c,int start_x,int start_y,int start_val) {
int cur_n = n-1;
//분할할 길이
int len = (int) Math.pow(2,cur_n);
int row=0,col=0;
int val_wieght = 0;
int[][] loc = {{0,0},
{0,1},
{1,0},
{1,1}} ;
//영역 분할
for (int i = 0 ; i < 4 ; i++){
row = start_x + loc[i][0]*len;
col = start_y + loc[i][1]*len;
//해당 row, col 이 가지고 있는 순번을 구하기 위한 가중치
val_wieght = i*len*len;
// 해당 영역에 좌표가 있다면 out
if(r>= row && r<row+len && c>=col && c < col+len ){
break;
}
}
//row col 이 해당 좌표라면 return
if(r==row && c == col ){
return start_val +val_wieght;
}
//아니라면 다시 해당영역 분할
return solution(cur_n,r,c,row,col,start_val+val_wieght);
}
}
결과
배열을 사용했다면, 무지 성으로 풀었을 것 같은데, 배열 없이 구하자니, 규칙성을 구하고 표현하는 부분에서 시간을 오래 쓴 듯하다. 1) 해당 목적지를 어떻게 하면 빠르게 찾을까? 와 2) 해당 목적지에서 요구하는 값을 어떻게 구할 것인가? 를 미리 정의하고 시작했다면 조금 더 시간 소모 없이 풀었을 것 같다.
포스팅에 문제가 있거나, 설명이 잘못된 부분 지적 환영합니다.
더 나은 퀄리티의 콘텐츠를 제공할 수 있도록 노력하겠습니다.
읽어주셔서 감사합니다.
'공부 > 코딩 테스트 준비' 카테고리의 다른 글
[백준] 11286 - 절댓값 힙 JAVA (0) | 2021.10.01 |
---|---|
[백준] 9019 - DSLR JAVA (0) | 2021.09.30 |
[백준] 16928 - 뱀과 사다리 게임 JAVA (0) | 2021.09.28 |
[백준] 1697 - 숨바꼭질 (0) | 2021.09.28 |
[백준] 2606 - 바이러스 JAVA (0) | 2021.09.26 |
- Total
- Today
- Yesterday
- 플루이드 와샬
- java
- 프로그래머스
- 프로그래머스 문제정복
- value annotation
- dml
- 유클리드-호제법
- 아기상어나쁜상어
- 백준
- 재귀
- 실패일기
- 파이썬
- 브루트포스
- 그래프 탐색
- 코딩테스트
- db
- 하루 회고
- looker core
- DFS
- looker instance 접속
- BFS
- 아기상어미워
- 9019
- Database
- 카카오
- Spring
- DP
- JNDI연동
- Python
- 자바
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |