[자바스크립트JS] 프로그래머스 - 택배 상자 꺼내기

[카테고리]

코딩테스트 연습 > 2025 프로그래머스 코드챌린지 2차 예선 > 택배 상자 꺼내기

 

[문제설명]

1 ~ n의 번호가 있는 택배 상자가 창고에 있습니다. 당신은 택배 상자들을 다음과 같이 정리했습니다.

왼쪽에서 오른쪽으로 가면서 1번 상자부터 번호 순서대로 택배 상자를 한 개씩 놓습니다. 가로로 택배 상자를 w개 놓았다면 이번에는 오른쪽에서 왼쪽으로 가면서 그 위층에 택배 상자를 한 개씩 놓습니다. 그 층에 상자를 w개 놓아 가장 왼쪽으로 돌아왔다면 또다시 왼쪽에서 오른쪽으로 가면서 그 위층에 상자를 놓습니다. 이러한 방식으로 n개의 택배 상자를 모두 놓을 때까지 한 층에 w개씩 상자를 쌓습니다.

 

( ... 이하 생략 )

 

자세한 문제설명은 아래 링크 참고

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

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

[문제풀이]

function solution(n, w, num) {
    var answer = 0;
    
    let lastRow  = Math.ceil(n/w);
    let lastCol  = n%w === 0 ? w : n%w;
    let lastLCol = lastRow%2 === 1 ? lastCol : (w+1)-lastCol;
    
     for(let numRow=0; numRow<lastRow; numRow++) {
        if(((numRow)*w)+1<=num && num<=(numRow+1)*w) {
            let numCol  = num%w === 0 ? w : num%w;
            let numLCol = numRow%2 === 0 ? numCol : (w+1)-numCol;
            
            answer = lastRow - numRow;
            if(lastRow%2 === 1 && lastLCol < numLCol) answer -= 1;
            if(lastRow%2 === 0 && lastLCol > numLCol) answer -= 1;
            
            break;
        }
    }

    return answer;
}

 

 

<변수 설명>

n 마지막 상자 번호
w 최대 행의 수 (가로로 깔 수 있는 최대 상자의 수)
num 꺼내야하는 상자 번호
answer 최종 제출 답 (num을 꺼내기위해 필요한 상자의 개수)

 

lastRow 마지막 상자가 위치한 행 (총 상자탑의 층수) 
lastCol (해당 줄 시작지점기준으로) 마지막 상자가 위치한 열
lastLCol (왼쪽기준으로) 마지막 상자가 위치한 열

 

numRow 꺼내야하는 상자가 위치한 행
numCol  (해당 줄 시작지점기준으로) 꺼내야하는 상자가 위치한 열
numLCol (왼쪽기준으로) 꺼내야하는 상자가 위치한 열

 

이렇게 말로 설명을 하면 이해가 안가니.. 그림으로 설명하면 아래와 같다.

 

<코드설명>

 

일단 코드의 대략적인 흐름은 아래 순서에 맞춰 작성해나갔다.

 

① 마지막 상자가 있는 위치 구하기

let lastRow  = Math.ceil(n/w);
let lastCol  = n%w === 0 ? w : n%w;
let lastLCol = lastRow%2 === 1 ? lastCol : (w+1)-lastCol;
  • 상자의 행 위치 (lastRow)
    • Math.ceil 자바스크립트 함수를 이용하여 마지막 상자의 층수를 구한다.
  • 상자의 열 위치 (lastLCol)
    1. 일단 마지막상자 번호(n)와 최대 행의 수(w)를 나눈 나머지를 구한다.
    2. 나머지가 0인 경우가 나오는데, 이는 최대 행의 수(w)의 배수라는 것을 의미한다. 그와 동시에 해당 줄의 최대값을 의미한다. 
      => 그렇기에 마지막상자 번호(n)가 최대 행(w)의 배수일 경우엔, 마지막 상자 열 위치(lastCol)이 최대값이 될 수 있도록 최대 행(w)값을 준다.
    3. 마지막으로, 왼쪽기준으로 마지막상자의 열 위치(lastLCol)를 구한다.
      - 마지막상자가 위치한 행(lastRow)이 '홀수층'일 경우, 정방향이므로 lastRow 값 그대로 지정한다.
      - 마지막상자가 위치한 행(lastRow)이 '짝수층'일 경우, 역방향이므로 최대 행(w)에서 lastRow를 빼준 값을 지정해줘야한다.
      ( ※ 여기에서 w에다가 1을 더해주는 이유열의 시작위치가 첫번째 1이기 때문이다! (ex. 달력 월 개수 구하기) )

② 꺼내려는 상자가 있는 위치 구하기

for(let numRow=0; numRow<lastRow; numRow++) {
    if(((numRow)*w)+1<=num && num<=(numRow+1)*w) {
        let numCol  = num%w === 0 ? w : num%w;
        let numLCol = numRow%2 === 0 ? numCol : (w+1)-numCol;

        answer = lastRow - numRow;
        if(lastRow%2 === 1 && lastLCol < numLCol) answer -= 1;
        if(lastRow%2 === 0 && lastLCol > numLCol) answer -= 1;

        break;
    }
}
  • 반복문을 이용해 모든 층을 순회하여 꺼내려는 상자(num)의 위치를 구한다. (for)
    ( 반복문의 범위 : 첫번째층(0) 이상 ~ 마지막층(lastRow) 미만 )
  • 상자의 행 위치 (numRow)
    • 조건문을 이용해 꺼내려는 상자(num)이 위치한 층을 찾아낸다. (if)
    • 조건은 여러 행에 공통적으로 적용되는 상자번호 범위 규칙을 찾아내 작성하였다.
      - 첫번째 행: 1 ≤ num ≤ 5            ======> 0w+1≤ num  ≤ 1w
      - 두번째 행: 6 ≤ num  ≤ 10         ======> 1w+1≤ num  ≤ 2w
      - 세번째 행: 11 ≤ num  ≤ 15       ======> 2w+1≤ num  ≤ 3w
      - 네번째 행: 16 ≤ num  ≤ 20       ======> 3w+1≤ num  ≤ 4w

      => 이를 통해, ( numRow*w ) + 1 num ≤ ( numRow+1 ) * w 라는 공통 조건을 유추해낼 수 있다.
  • 상자의 열 위치 (numLCol)
    • 꺼내려는 상자의 열 위치 구하는 방식 역시 위에 작성한 ⓛ번과 유사하다.
    • 다른 점은 층의 홀수/짝수 여부를 구하는 조건식이 다르다는 점이다.
      - 위에는 lastRow%2 === 1 일때 정방향인것으로 지정했지만, 여기서는 numRow%2 === 0 일때 정방향으로 지정했다는 점이 다르다.
      => 그 이유는 lastRow는 무조건 1(층)부터 시작하지만, numRow는 시작값이 0이기때문이다!
  • 최종 제출 답(answer)
    • (마지막 층에 상자의 개수가 다 있다는 전제 하에) 꺼내려는 상자(num)를 꺼내기위해 필요한 상자의 개수는
      마지막 상자의 행 위치(lastRow) - 꺼내려는 상자의 행 위치(numRow) 이다.
    • 하지만 (위의 그림처럼) 마지막 층엔 상자가 다 존재하지 않는 예외적인 경우가 있다.
      => 그렇기에 다 존재하지 않는 경우에 따른 마지막 층에 놓인 상자를 제외시켜주는 연산식을 작성해줘야한다.
    • 예외적인 상황은 아래와 같다. 
      1. 마지막 상자의 행 위치(lastRow)가 정방향일 경우, 마지막 상자의 열 위치(lastLCol)가 꺼내려는 상자의 열 위치(numLCol)보다 작을 경우 
      2. 마지막 상자의 행 위치(lastRow)가 역방향일 경우, 마지막 상자의 열 위치(lastLCol)가 꺼내려는 상자의 열 위치(numLCol)보다 클 경우

        이렇게 설명하면 이해가 안되니 그림을 이용하여 설명하면 아래와 같다.

∴ 그래서 위의 예외적인 경우엔 꺼내려는 상자의 맨 위에는 상자가 존재하지 않으므로 1을 빼줘야한다!! 

 

  • 최종 제출답(answer)에 값이 선언되면 break를 이용해 그 즉시 반복문을 멈춘다.

 

 

이것으로 코드 설명 끝..!

'자바스크립트 > 코딩테스트' 카테고리의 다른 글

[Python] 쓱 최대로 할인 적용하기  (0) 2025.03.24