본문 바로가기
Algorithm/Programmers

[Programmers] Lv.1 | 택배 상자 꺼내기 | Java

by unknownomad 2025. 11. 6.

문제

https://school.programmers.co.kr/learn/courses/30/lessons/389478

 

풀이

class Solution {
    public int solution(int n, int w, int num) {

        // 상자는 번호가 1번부터 시작하지만, 컴퓨터는 0부터 세기 때문에
        // 계산하기 편하게 num - 1을 해줌
        // 예: 1번 상자는 0번째로, 7번 상자는 6번째로 생각
        int floor = (num - 1) / w;  // num이 몇 번째 층(0층부터)에 있는지
        int pos = (num - 1) % w;    // 그 층 안에서 왼쪽부터 몇 번째 칸인지 (0부터 시작)

        // 층의 쌓이는 방향이 짝수층은 왼쪽→오른쪽, 홀수층은 오른쪽→왼쪽이라서
        // 홀수층일 때는 좌우 순서를 반대로 바꿔줘야 함
        // 예: w=6일 때, 1층은 12 11 10 9 8 7 순서이므로
        // 오른쪽부터 0, 1, 2... 으로 번호가 매겨짐
        int col = (floor % 2 == 0) ? pos : (w - 1 - pos);

        // 전체 층 개수를 구함
        // 예: 상자가 22개고 한 줄에 6개씩 놓으면 4층(0,1,2,3층)이 생김
        // (22 + 6 - 1) / 6 은 올림 효과를 줘서 마지막에 덜 찬 층도 포함시킴
        int totalFloors = (n + w - 1) / w;

        // 자기 자신 상자도 꺼내야 하니까 처음부터 1개로 시작
        int count = 1;

        // 내 상자(num) 위에 있는 층들을 위에서부터 하나씩 확인
        for (int f = floor + 1; f < totalFloors; f++) {

            // f층의 첫 번째 상자 번호를 구함
            // 예: 0층은 1번, 1층은 7번, 2층은 13번...
            int start = f * w + 1;

            // f층의 마지막 상자 번호를 구함
            // 단, 마지막 층은 상자가 부족할 수도 있으니까 n을 넘지 않게 조정
            // 예: (f+1)*w = 24지만 실제 상자는 22번까지면 22로 제한
            int end = Math.min((f + 1) * w, n);

            // 이 층에 실제로 몇 개의 상자가 있는지 계산
            // 예: 13~18번 → 6개 (18 - 13 + 1)
            int width = end - start + 1;

            // 열(column) 계산
            // 짝수층은 왼→오 방향이니까 그대로 col 사용
            // 홀수층은 오→왼이라 col을 반대로 계산해야 함
            // 단, 기준은 항상 한 줄 전체 크기(w)를 기준으로 해야 함
            // (마지막 층처럼 상자가 적을 때도 열 위치 계산이 틀어지지 않게 하기 위해)
            int boxCol = (f % 2 == 0) ? col : (w - 1 - col);

            // 만약 이 층에 boxCol 위치가 실제로 존재하면 (즉, 상자가 있다면)
            // 그 상자도 꺼내야 함
            if (boxCol >= 0 && boxCol < width) {
                count++;
            }
        }

        // 꺼내야 하는 상자의 총 개수를 반환
        return count;
    }
}

코드 전체 흐름 설명

  1. num이 어느 층, 어느 칸에 있는지 계산
    • / w → 몇 번째 층인지
    • % w → 층 안에서 몇 번째 칸인지
    • 층 방향이 바뀌면 칸 번호도 반대로 계산해야 함
  2. 전체 층 수 계산
    • 마지막 층이 w개보다 적을 수 있으니까 올림 처리
  3. 위층부터 끝층까지 같은 열(column)에 있는 상자를 찾음
    • 층마다 방향이 다르므로 열 번호를 다시 계산해야 함
    • 마지막 층은 상자가 부족할 수 있으니 실제 상자 개수(width)로 범위를 제한
  4. 존재하는 상자만 count

이해 포인트 요약

코드 의미 이유
(num - 1) 번호를 0부터 맞춤 컴퓨터는 0부터 시작함
/ w 몇 번째 층인지 한 층에 w개씩 있으니까
% w 층 안에서 몇 번째 칸인지 한 줄에서 나머지 칸 번호
(w - 1 - pos) 방향 반전 오른쪽→왼쪽으로 쌓일 때
(n + w - 1) / w 전체 층 개수 나누어떨어지지 않을 때 올림 처리
Math.min((f + 1) * w, n) 마지막 층 조정 상자가 부족할 수 있음
if (boxCol < width) 상자가 실제 존재하는 칸만 카운트 마지막 층은 폭이 작을 수 있음

public int solution(int n, int w, int num) {
    // 입력값 의미
    // n : 전체 상자 개수
    // w : 한 줄(한 층)에 놓는 상자 개수
    // num : 꺼내려는 상자의 번호

    // cnt : 꺼내야 하는 상자 개수 (자기 자신 포함)
    int cnt = 0;

    // num은 현재 꺼내려는 상자 번호
    // num이 n을 넘지 않을 동안 반복 (위층 상자가 남아 있는 동안)
    while (num <= n) {

        // (num - 1) % w : 현재 상자가 층 안에서 몇 번째 칸인지 (왼쪽부터 0 기준)
        // (w - ((num - 1) % w) - 1) : 오른쪽 끝에서부터 몇 칸 떨어져 있는지
        //
        // (w - ((num-1) % w) - 1) * 2 + 1
        //  → 위층에 쌓인 상자 중 같은 열(column)에 있는 상자의 “번호 차이” 계산
        //  → 즉, 이 식만큼 더하면 바로 위층에 있는 같은 열의 상자 번호가 됨
        //
        // num += ... : 위층의 같은 column 상자 번호로 이동
        num += (w - ((num - 1) % w) - 1) * 2 + 1;

        // 꺼내야 하는 상자 하나 발견했으니 카운트 증가
        cnt++;
    }

    // 결과 반환
    return cnt;
}

이 코드가 하는 일

이 코드는 층을 직접 계산하거나 시뮬레이션하지 않고,
위층에 있는 같은 위치의 상자 번호를 직접 점프해서 찾는 방식

즉, “한 층 위에 같은 열에 있는 상자 번호가 얼마인지”를
수식으로 한 줄에 표현해 놓은 것

 

동작 예시 (n = 22, w = 6, num = 8)

반복 num 값 의미 설명
시작 8 1층 처음 꺼낼 상자
1회차 num += (6 - ((8-1)%6) - 1)*2 + 1 → 8 + (6-1-1)*2 + 1 = 8 + 9 = 17 2층의 같은 열 상자 17번 상자도 꺼내야 함
2회차 num += (6 - ((17-1)%6) - 1)*2 + 1 → 17 + (6-4-1)*2 + 1 = 17 + 3 = 20 3층의 같은 열 상자 20번 상자도 꺼내야 함
3회차 num = 20 + (...) → 20 + (6 - ((20-1)%6) - 1)*2 + 1 = 20 + 7 = 27 27 > n(22) 반복 종료

결과: 꺼낸 상자 3개 (8, 17, 20)

 

왜 이런 식이 나왔는지

  1. (num - 1) % w
    → 현재 상자가 그 층에서 몇 번째 칸인지(0부터 센 위치)
  2. (w - ((num - 1) % w) - 1)
    → 그 상자가 오른쪽 끝에서 얼마나 떨어져 있는지
  3. 그 값에 *2 + 1을 하는 이유
    → 상자는 왼→오, 오→왼으로 번갈아 쌓이니까
    한 층 올라갈 때 반대쪽 끝으로 이동했다가 다시 같은 열로 돌아와야 함
    (그래서 ‘왕복 거리 ×2 + 1칸’ 이동)

이걸 다 합치면 “위층의 같은 열 상자까지 건너뛰는 거리”가 됨

댓글