[백준 3101] 토끼의 이동

최민길(Gale)·2023년 6월 19일
1

알고리즘

목록 보기
76/172

문제 링크 : https://www.acmicpc.net/problem/3101

이 문제는 주어진 격자의 규칙을 파악하면 풀 수 있는 문제입니다.

처음 이 문제를 이중 ArrayList를 이용하여 대각선 별로 데이터를 채워 넣는 방식으로 접근했으나 N이 10만 가량 되기 때문에 NXN을 모두 채우면 메모리 초과가 발생합니다. 따라서 규칙을 찾아 적용하는 방법을 생각해야 합니다.

https://nahwasa.com/entry/%EC%9E%90%EB%B0%94-%EB%B0%B1%EC%A4%80-3101-%ED%86%A0%EB%81%BC%EC%9D%98-%EC%9D%B4%EB%8F%99-java
저는 위의 블로그를 참고하여 문제를 풀었습니다. 현재 토끼의 위치를 다음과 같이 나타낼 수 있습니다.

토끼의 위치 = 대각선 시작값 + 상하좌우로 이동한 보정값

이렇게 진행한 이유는 각 대각선의 시작값이 공차가 n인 등차수열이기 때문입니다. (a_n = a_n-1 + n) 각 대각선의 시작값은 1,2,4,7... 이런 식으로 확장하기 때문에 등차수열이 성립합니다.

하지만 주의할 점은 증가하는 등차수열은 N번째 대각선까지만 적용된다는 것입니다. N번째 대각선 이후는 감소하는 등차수열이 됩니다. (a_n = a_n+1 - n)

이를 토대로 dp[i] = i+1번 대각선(제일 처음 1의 경우 1번 대각선으로 간주했을 때)의 시작값 으로 설정할 수 있으며 값을 모두 채워넣을 수 있습니다. 이 경우 i는 2N-2까지 진행되기 때문에 메모리 초과가 발생하지 않습니다.

이후 보정값에 대해 알아봅시다. 토끼의 좌표가 y,x라고 했을 때 토끼는 y+x+1번 대각선에 위치합니다. 이 때 i+1이 홀수일 경우(1번, 3번 ... 대각선) 아래에서 위로 증가하는 방향을 나타내며 i+1이 짝수일 경우(2번, 4번 ... 대각선) 위에서 아래로 감소하는 방향을 나타냅니다.

하나의 예를 들어봅시다. 만약 N이 충분히 큰 상황에서 (y,x) = (0,1)의 값이 아래로 이동하면 어떤 값을 나타낼까요? 나타나는 좌표는 (1,1)으로 i가 짝수인 대각선으로(i = y+x) 이동함과 동시에 대각선의 시작에서 1칸 앞서게 됩니다. 그럼 오른쪽으로 이동하면 어떻게 될까요? (0,2)로 이동함과 동시에 대각선의 시작에서 2칸 앞서게 됩니다.

그럼 i가 홀수인 대각선으로 이동하는 경우도 살펴봅시다. (y,x) = (0,2)의 값이 아래로 이동할 경우 i가 홀수인 대각선으로 이동하며 (1,2)의 좌표를 가집니다. 이 때 시작값에서 1만큼 떨어지게 됩니다. 이어서 오른쪽으로 이동할 경우 (0,3)으로 이동하며 시작값에서 0만큼 떨어지게 됩니다.

여기서 규칙을 발견하셨나요? 즉 (y,x)가 홀수 대각선에 존재하는 경우, 즉 (y+x)%2==1인 경우 짝수 대각선으로 이동하면서 대각선 시작값보다 y만큼 멀어지게 됩니다. 반대로 (y,x)가 짝수 대각선에 존재하는 경우, 즉 (y+x)%2==0인 경우 홀수 대각선으로 이동하면서 대각선 시작값보다 x만큼 멀어지게 됩니다. 따라서 보정값은 (y+x)%2==0 ? x : y로 정의할 수 있습니다.

하지만 이 값 역시 i+1이 N까지만 적용됩니다. 이후는 감소하는 등차수열이기 때문에 적용이 다르게 되기 때문이죠. 감소하는 경우는 다르게 보아야 합니다. 만약 N=6인 격자에서 (y,x) = (2,4)일 경우 아래로 이동하는 경우를 보겠습니다. i가 짝수 대각선에서 홀수로 이동하며 (3,4)로 이동하며 시작값에서 1만큼 떨어집니다. 오른쪽으로 이동하는 경우 (2,5)로 이동하며 시작값에서 0만큼 떨어집니다.

i가 홀수인 경우도 보겠습니다. (y,x) = (3,4)에서 아래로 이동하는 경우 (4,4)가 되며 시작값에서 1만큼 떨어집니다. 오른쪽으로 이동하는 경우 (3,5)가 되며 시작값에서 2만큼 떨어집니다.

여기서도 규칙을 발견하셨나요? 감소하는 수열에서는, 즉 y+x(=i) >=N 인 경우 짝수 대각선에서 홀수로 이동할 경우 시작값보다 N-1-y만큼 멀어지게 되며, 홀수에서 짝수로 이동하는 경우 시작값보다 N-1-x만큼 멀어지게 됩니다. 여기서 포인트는 감소하는 수열에서는 증가하는 수열과 반대로 짝수에서 홀수로 이동하는 경우 y값에 영향을 받으며 홀수에서 짝수로 이동하는 경우 x값에 영향을 받는다는 것입니다. 따라서 이 경우 단순히 값을 다르게 처리하는 것 뿐만 아니라 y와 x값을 반대로 적용해야 합니다.

다음은 코드입니다.

import java.util.*;
import java.io.*;

class Main{
    static int N,K;
    static long[] dp;

    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        K = Integer.parseInt(st.nextToken());
        String str = br.readLine();

        // dp[i] = i+1번 대각선의 시작값
        // 각 대각선의 시작 수열 : 1,2,4,7,... --> a_n = a_n-1 + n
        // i+1 <= N 까지는 증가하는 수열
        dp = new long[2*N-1];
        dp[0] = 1;
        for(int i=1;i<N;i++) dp[i] = dp[i-1]+i;

        // i+1 > N 부터는 감소하는 수열
        // a_n = a_n+1 - n
        dp[2*N-2] = (long) N *N;
        for(int i=2;i<N;i++) dp[2*N-i-1] = dp[2*N-i]-i;

        Rabbit rabbit = new Rabbit();
        for(int i=0;i<K;i++){
            char d = str.charAt(i);
            rabbit.move(d);
            rabbit.setScore();
        }

        System.out.println(rabbit.score);
    }

    static class Rabbit{
        int y = 0;
        int x = 0;
        long score = 1;

        Rabbit(){}

        void move(char d){
            switch (d){
                case 'U':
                    y--;
                    break;
                case 'D':
                    y++;
                    break;
                case 'L':
                    x--;
                    break;
                case 'R':
                    x++;
                    break;
            }
        }

        void setScore(){
            score += dp[y+x] + correction(y,x);
        }

        int correction(int y, int x){
            if (y+x >= N) {
                y = N-1-y;
                x = N-1-x;
                int tmp = y;
                y = x;
                x = tmp;
            }
            return (y+x)%2==0 ? x : y;
        }
    }
}

profile
저는 상황에 맞는 최적의 솔루션을 깊고 정확한 개념의 이해를 통한 다양한 방식으로 해결해오면서 지난 3년 동안 신규 서비스를 20만 회원 서비스로 성장시킨 Software Developer 최민길입니다.

0개의 댓글

관련 채용 정보