본문 바로가기
Algorithm(코딩테스트)/코딩테스트 | 기초 개념 시리즈

[JAVA] 2차원 배열 + dx/dy 테크닉

by 카랑현석 2024. 11. 27.

기본 문법

- 기본 문법은 1차원 배열과 유사하다.



dx, dy 테크닉

dx, dy 테크닉은 어떤 2차원 좌표에서 이동을 할 때 간단하게 사용할 수 있는 알고리즘 기법이다.

 

예를 들어, 아래 그림처럼 4*5로 된 격자에 빨간 점에서 상,하,좌,우 로 이동하려면 어떻게 해야 할까?


if-else 문을 써서 이동 시켜도 되겠지만, 더 간단한 방법이 있다.

 

#출발 지점 설정
startR = 0;
startC = 0; 

# 이동 방향
dir_num = 0

# 방향을 저장하는 순서는 문제에 따라 사용자가 지정 (현재는 우, 상, 좌, 하로 이동)
#     0  1   2  3
dc = [1, 0, -1, 0]
dr = [0, -1, 0, 1]

# 조건에 따라 이동
nextR = startR + dr[dir_num];
nextC = startC + dc[dir_num];

 

 

예제 적용

0. 문제

5*4 격자에 다음과 같이 숫자를 적으려면 어떻게 적어야 할까?

 

1. 규칙 찾기

- 처음에는 우측으로 이동한다.

- 벽에 부딪히면 아래로 이동한다.

- 또 벽에 부딪히면 좌측으로 이동한다.

- 또 벽에 부딪히면 위쪽으로 이동한다.

 

- 또 벽에 부딪히면 다시 우측으로 이동한다.

- 또 벽에 부딪히면 다시 아래로 이동한다.

- 또 벽에 부딪히면 다시 좌측로 이동한다.

- 또 벽에 부딪히면 다시 위쪽로 이동한다.

 

... 반복반복

 

이때, 이미 작성된 곳은 더 이상 방문하지 않는다.

 

01234567
달팽이 게임 구현 과정

 

2. 구현

1) 상하좌우 이동 구현

현재 위치를 (r,c) 라고 할 때,

 

- 우측으로 이동하면 c를 1 더해주면 된다. (r, c+1)

- 아래로 이동하면 r을 1 더해주면 된다. (r+1, c)

- 좌측으로 이동하면 c를 1 빼주면 된다. (r, c-1)

- 위로 이동하면 r을 1 빼주면 된다. (r-1, c)

 

이 이동을 코드로 구현하면 아래와 같이 간단하게 구현할 수 있다.

- dr은 r의 이동량, dc는 c의 이동량이다.

- moveIdx는 이동하는 방향을 의미하는 Index 번호이다.

우측으로 이동할거면 moveIdx를 0으로 두고, nextR = curR + dr[ moveIdx]; nextC = curC + dc[moveIdx]; 을 하면 된다.

    // 우 하 좌 상 순서 움직임 구현
    int[] dr = {0,1,0,-1}; 
    int[] dc = {1,0,-1,0};
    int moveIdx = 0; // 0 : 우, 1 : 하, 2 : 좌, 3 : 상 으로 이동

 

2) 회전 조건
벽에 부딪히거나 그 다음 위치에 값이 이미 있는 경우에는 moveIdx에 1을 더해 이동하는 방향을 바꿔준다.
우리는 1.규칙 찾기 단계에서 우측 -> 아래 -> 좌측 -> 위쪽 으로 이동한다는 것을 발견하고 이동 구현할 때 이 순서대로 구현했었다.

 

1. 벽에 부딪힌다는 것은 배열의 범위 밖을 벗어난다는 의미이고,

2. 이미 있는 값이라는 것은 초기 배열 선언 시 모두 0이 저장이 되므로 0이 아닌 수가 있으면 이미 있는 값으로 판단하면 된다.

 

저 두 경우에는 moveIdx의 값에서 1을 더해서 다음 방향으로 이동하도록 한다.

그런데, moveIdx가 4가 되면 어떻게 될까? dr[4]와 dc[4]는 없으므로 오류가 발생할 것이다.

우측 -> 아래 -> 좌측 -> 위쪽 의 이동을 마치면 다시 우측부터 시작해야 한다.

 

이 문제를 해결하기 위해 아래 간단한 코드로 해결할 수 있다.

// 1안
moveIdx = ++moveIdx % 4;

// 2안
nextR = curR + dr[moveIdx%4];

 

지금까지 언급한 것을 모두 코드로 옮기면 아래와 같다.

 

import java.util.*;

class Solution
{
	public static void main(String args[]) throws Exception
	{
        		// input //
		Scanner sc = new Scanner(System.in);
		
		int T = Integer.parseInt(sc.nextLine());
		for(int testCase=1; testCase<=T; testCase++) {
			int N = Integer.parseInt(sc.nextLine());
			
			// logic // 
			int[][] board = new int[N][N];
			// 1) 우 하 좌 상 순서 움직임 구현
			int[] dr = {0,1,0,-1}; 
			int[] dc = {1,0,-1,0};
			int moveIdx = 0; // 0 : 우, 1 : 하, 2 : 좌, 3 : 상 으로 이동
			// 현재 위치
			int curR = 0; 
			int curC = 0;
			// 채울 번호
			int fillNum = 2;
			
			// 초기에는 무조건 0,0에 1을 채운다.
			board[0][0] = 1;
			
			while(fillNum <= N*N) {
				// 1) 이동 구현
				int nextR = curR + dr[moveIdx%4];
				int nextC = curC + dc[moveIdx%4];
                // 2) 벽에 닿을 조건(배열 범위 밖으로 가는 조건 예외 처리)
				if(nextR < 0 || nextC < 0 || nextR >= N || nextC >= N) {
					moveIdx++;
					continue;
				}
                // 2) 이미 있는 값에 대한 예외 처리
				if(board[nextR][nextC] != 0) {
					moveIdx++;
					continue;
				}
				
				board[nextR][nextC] = fillNum;
				fillNum++;
				curR = nextR;
				curC = nextC;
			}

			
			// 정답 출력
			System.out.println("#"+testCase);
			
			for(int r=0; r<N; r++) {
				for(int c=0; c<N; c++) {
					System.out.print(board[r][c]+" ");
				}
				System.out.println();
			}
			
		} // tc
    }
}

 

 

연습 문제

2차원 배열과 dx, dy 테크닉을 익히기 좋은 문제를 선별했다.

 

코드트리_대각선으로 숫자 채우기 https://www.codetree.ai/missions/4/problems/diagonal-numbering/description #2차원 배열
#dx,dy 테크닉
코드트리_격자 반대로 채우기 https://www.codetree.ai/missions/4/problems/grid-anti-fill/description #2차원 배열
#dx,dy 테크닉
SWEA_1954. 달팽이 숫자 https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV5PobmqAPoDFAUq&categoryId=AV5PobmqAPoDFAUq&categoryType=CODE&problemTitle=달팽이&orderBy=FIRST_REG_DATETIME&selectCodeLang=ALL&select-1=&pageSize=10&pageIndex=1 #2차원 배열
#dx,dy 테크닉