코딩테스트 스택 풀이: 크레인 인형뽑기 처리법

··설명 약 9분·작성: 해비·블로그 소개

읽기 전에 알아두면 좋아요

아래 개념이 익숙하면 본문을 바로 읽으세요. 낯선 개념만 링크에서 복습하면 됩니다.

개념과 예제는 실행하지 않아도 읽을 수 있습니다. 내 컴퓨터·터미널에서 직접 실행하는 과정은 선택 실습입니다.

과정 위치와 선택 복습 안내

과정 위치: 자료구조 개념과 문제 · 문제

과정 위치는 이 글이 속한 묶음입니다. 이 단계의 모든 개념을 먼저 읽을 필요는 없습니다. 위에 연결한 개념 중 낯선 내용만 복습하세요. 문제의 조건·상태·갱신 순서는 익숙한 언어로 설명하면 됩니다.

JavaScript로 문법·실행 복습하기 (선택)

개념 글의 코드는 JavaScript 예시입니다. 문제 화면에서는 JavaScript·Python·Java·C++·C# 중 익숙한 언어의 함수를 작성하세요. 본문의 Node.js 입출력 예시는 로컬 실행을 위한 별도 안내입니다.

크레인 인형뽑기 문제는 인형 배열과 뽑기 위치를 받아 무엇의 수를 구하나요?

인형 배열과 뽑기 위치를 입력받아 사라진 인형의 수를 구하는 문제입니다. 2차원 배열·스택의 기본을 준비하고, 아래 입력·반환 계약을 확인해 직접 풀어보세요.

적용 조건·범위: [제한사항] board 배열은 2차원 배열로 크기는 5 x 5 이상 30 x 30 이하입니다. board의 각 칸에는 0 이상 100 이하인 정수가 담겨있습니다. 0은 빈 칸을 나타냅니다. 1 ~ 100의 각 숫자는 각기 다른 인형의 모양을 의미하며 같은 숫자는 같은 모양의 인형을 나타냅니다. moves 배열의 크기는 1 이상 1,000 이하입니다. moves 배열 각 원소들의 값은 1 이상이며 board 배열의 가로 크기 이하인 자연수입니다.

본문 근거: 프로그래머스 공식 문제: 크레인 인형뽑기 게임

문제 설명

게임개발자인 죠르디는 크레인 인형뽑기 기계를 모바일 게임으로 만들려고 합니다.죠르디는 게임의 재미를 높이기 위해 화면 구성과 규칙을 다음과 같이 게임 로직에 반영하려고 합니다.

크레인 인형뽑기 JS 풀이: 스택으로 사라지는 인형 처리 핵심 개념을 설명하는 첫 번째 본문 이미지

게임 화면은 1 x 1 크기의 칸들로 이루어진 N x N 크기의 정사각 격자이며 위쪽에는 크레인이 있고 오른쪽에는 바구니가 있습니다. (위 그림은 5 x 5 크기의 예시입니다). 각 격자 칸에는 다양한 인형이 들어 있으며 인형이 없는 칸은 빈칸입니다. 모든 인형은 1 x 1 크기의 격자 한 칸을 차지하며 격자의 가장 아래 칸부터 차곡차곡 쌓여 있습니다. 게임 사용자는 크레인을 좌우로 움직여서 멈춘 위치에서 가장 위에 있는 인형을 집어 올릴 수 있습니다. 집어 올린 인형은 바구니에 쌓이게 되는 데, 이때 바구니의 가장 아래 칸부터 인형이 순서대로 쌓이게 됩니다. 다음 그림은 [1번, 5번, 3번] 위치에서 순서대로 인형을 집어 올려 바구니에 담은 모습입니다.

크레인 인형뽑기 JS 풀이: 스택으로 사라지는 인형 처리 적용 흐름을 설명하는 두 번째 본문 이미지

만약 같은 모양의 인형 두 개가 바구니에 연속해서 쌓이게 되면 두 인형은 터뜨려지면서 바구니에서 사라지게 됩니다. 위 상태에서 이어서 [5번] 위치에서 인형을 집어 바구니에 쌓으면 같은 모양 인형 두 개가 없어집니다.

크레인 인형뽑기 JS 풀이: 스택으로 사라지는 인형 처리 참고 내용을 설명하는 본문 이미지 3

크레인 작동 시 인형이 집어지지 않는 경우는 없으나 만약 인형이 없는 곳에서 크레인을 작동시키는 경우에는 아무런 일도 일어나지 않습니다. 또한 바구니는 모든 인형이 들어갈 수 있을 만큼 충분히 크다고 가정합니다. (그림에서는 화면표시 제약으로 5칸만으로 표현하였음)

게임 화면의 격자의 상태가 담긴 2차원 배열 board와 인형을 집기 위해 크레인을 작동시킨 위치가 담긴 배열 moves가 매개변수로 주어질 때, 크레인을 모두 작동시킨 후 터트려져 사라진 인형의 개수를 return 하도록 solution 함수를 완성해주세요

[제한사항]
board 배열은 2차원 배열로 크기는 5 x 5 이상 30 x 30 이하입니다. board의 각 칸에는 0 이상 100 이하인 정수가 담겨있습니다. 0은 빈 칸을 나타냅니다. 1 ~ 100의 각 숫자는 각기 다른 인형의 모양을 의미하며 같은 숫자는 같은 모양의 인형을 나타냅니다. moves 배열의 크기는 1 이상 1,000 이하입니다. moves 배열 각 원소들의 값은 1 이상이며 board 배열의 가로 크기 이하인 자연수입니다.

[[0,0,0,0,0],
 [0,0,1,0,3],
 [0,2,5,0,1],
 [4,2,4,4,2],
 [3,5,1,3,1]]

[1,5,3,5,1,2,1,4]

원자료와 확인 범위

2026-10-12에 제공된 학습 자료 섹션 6. 자료구조(스택, 큐).pdf의 3·4쪽 ‘크레인 인형뽑기(카카오 기출)’와 문제 조건·예제를 대조했습니다. 같은 ZIP의 해당 문제 TXT는 풀이 코드 자료입니다. 이 자료의 최초 출제 원문·저자·공개 배포 주소까지 확인한 것은 아닙니다.

프로그래머스 공식 문제: 크레인 인형뽑기 게임에서 board·moves 조건과 예제 결과 4를 직접 대조했습니다(2026-10-12). 아래 함수 호출과 사이트 제출 방식은 구분하세요.

이번 대조는 원자료의 문제 조건과 예제 확인입니다. 이 글의 전체 코드를 새로 실행하거나 공식 채점에 제출한 결과를 뜻하지 않습니다. 아래 예제·자가 확인 항목은 직접 실행해 기대 출력과 비교하는 기준입니다.

직접 풀어보기

먼저 선택한 언어의 입력·반환 계약에 맞춰 작성해 보세요. 힌트 받기와 정답과 비교하기는 코드를 실행하지 않는 AI 분석입니다. 해답·해설은 아래에서 필요할 때 펼칠 수 있습니다.

0 / 6,000바이트

solution(board, moves)에 board(정수 2차원 배열), moves(정수 배열)를 전달하고 정수를 반환합니다. 아래 문제의 유효 입력 조건을 따릅니다. 표준 입력과 출력 코드는 작성하지 않습니다. board는 집은 칸을 0으로 바꾸어도 됩니다. Tab은 다음 항목으로 이동합니다. 입력 영역 오른쪽 아래를 끌어 높이를 조절할 수 있습니다.

비밀키·개인정보는 입력하지 마세요. 코드는 이 페이지에만 유지됩니다.

해답·해설 보기
JavaScript 해답
function solution(board, moves) {
    const basket = [];
    let answer = 0;
    for (const move of moves) {
        const col = move - 1;
        for (let row = 0; row < board.length; row++) {
            const doll = board[row][col];
            if (doll === 0) continue;
            board[row][col] = 0;
            if (basket.length && basket[basket.length - 1] === doll) {
                basket.pop();
                answer += 2;
            } else basket.push(doll);
            break;
        }
    }
    return answer;
}

solution(board, moves)에 board(정수 2차원 배열), moves(정수 배열)를 전달하고 정수를 반환합니다. 아래 문제의 유효 입력 조건을 따릅니다. 표준 입력과 출력 코드는 작성하지 않습니다. board는 집은 칸을 0으로 바꾸어도 됩니다.

내가 푼 풀이

아래는 내가 처음 작성한 전체 코드입니다. 이후 단계별 설명에서는 이 코드를 기준으로 각 부분이 어떤 역할을 하는지 하나씩 살펴봅니다.

function solution(board, moves) {
  let arr = [];
  let answer = 0;
  let moveIndex = 0;

  for (let index of moves) {
    index--;
    while (moveIndex < board.length && board[moveIndex][index] === 0) {
      moveIndex++;
    }

    if (moveIndex < board.length && board[moveIndex][index] !== 0) {
      if (board[moveIndex][index] === arr.at(-1)) {
        answer += 2;
        arr.pop();
      } else {
        arr.push(board[moveIndex][index]);
      }
      board[moveIndex][index] = 0;
    }
    moveIndex = 0;
  }

  return answer;
}

먼저 내가 작성한 풀이는 문제를 있는 그대로 구현하는 방식에 가깝습니다. 크레인이 특정 열을 선택하면, 해당 열의 가장 위에서부터 인형을 하나 찾고, 그 인형을 바구니에 담는 흐름을 그대로 코드로 옮겼습니다.

이 풀이의 전체 흐름은 다음과 같이 나눌 수 있습니다.

1단계는 크레인이 이동한 열에서 집을 인형을 찾는 과정입니다. moves 배열의 각 값은 열 번호이므로, 1을 빼서 실제 인덱스로 변환한 뒤 위에서부터 탐색합니다.

for (let index of moves) {
  index--;
  while (moveIndex < board.length && board[moveIndex][index] === 0) {
    moveIndex++;
  }

이 코드는 선택된 열에서 값이 0이 아닌 지점을 찾을 때까지 위에서 아래로 내려가는 역할을 합니다. 즉, 크레인이 집을 수 있는 가장 위 인형을 찾는 단계입니다.

2단계는 인형을 바구니에 담으면서 바로 이전 인형과 비교하는 과정입니다. 바구니 역할을 하는 arr 배열의 마지막 값과 현재 집은 인형을 비교합니다.

if (moveIndex < board.length && board[moveIndex][index] !== 0) {
  if (board[moveIndex][index] === arr.at(-1)) {
    answer += 2;
    arr.pop();
  } else {
    arr.push(board[moveIndex][index]);
  }
  board[moveIndex][index] = 0;
}

여기서 같은 인형이 연속으로 들어오는 경우를 직접 처리합니다. 같은 경우에는 바구니에서 이전 인형을 제거하고, 제거된 인형의 개수를 answer에 더합니다.

마지막으로 각 move가 끝날 때마다 moveIndex를 다시 0으로 초기화하여, 다음 열 탐색이 항상 위에서부터 시작되도록 합니다.

이 풀이는 문제를 정확히 해결할 수 있지만, 크레인의 동작과 바구니의 상태를 모두 직접 관리해야 하기 때문에 코드 흐름이 다소 길어지고 복잡해지는 특징이 있습니다.

정답 풀이

다음은 같은 바구니 스택을 사용하면서 열 탐색을 for 반복문과 break로 정리한 풀이입니다. 문제 원문이 특정 정답 구현을 제공했다고 가정하지 않고 두 코드의 동작을 비교합니다.

function solution(board, moves) {
  let answer = 0;
  let stack = [];

  moves.forEach(pos => {
    for (let i = 0; i < board.length; i++) {
      if (board[i][pos - 1] !== 0) {
        let tmp = board[i][pos - 1];
        board[i][pos - 1] = 0;

        if (tmp === stack[stack.length - 1]) {
          stack.pop();
          answer += 2;
        } else {
          stack.push(tmp);
        }
        break;
      }
    }
  });

  return answer;
}

두 풀이의 바구니 처리에서 새 인형과 마지막 인형의 비교는 같습니다. 아래에서는 인형을 찾은 즉시 바구니를 갱신하고 해당 열 탐색을 끝내는 흐름을 확인합니다.

바구니에서는 항상 가장 마지막에 들어온 인형과 새로 들어온 인형을 비교합니다. 이 구조는 후입선출(LIFO) 특성을 가지는 스택과 완전히 동일합니다.

같은 스택의 상호작용 규칙은 JavaScript 올바른 괄호 스택 검증에서도 동일하게 확인할 수 있어, 스택 기반 사고를 비교하기 좋습니다.

첫 풀이의 arr도 push/pop/마지막 값 확인을 사용하는 스택입니다. 두 구현 모두 스택이며, 차이는 열 탐색의 while/for와 보조 인덱스 관리 방식입니다.

let answer = 0;
let stack = [];

이후 각 move에 대해 크레인이 집을 인형을 찾는 부분은 비교적 단순하게 처리합니다. 선택된 열을 위에서부터 순회하며 첫 번째 인형만 집습니다.

moves.forEach(pos => {
  for (let i = 0; i < board.length; i++) {
    if (board[i][pos - 1] !== 0) {
      let tmp = board[i][pos - 1];
      board[i][pos - 1] = 0;

인형을 집는 순간, 즉시 스택의 마지막 값과 비교합니다. 이때 조건 분기는 문제의 규칙과 1:1로 대응됩니다.

if (tmp === stack[stack.length - 1]) {
  stack.pop();
  answer += 2;
} else {
  stack.push(tmp);
}

같은 인형이면 제거하고 점수를 올리고, 다른 인형이면 그대로 스택에 쌓습니다. 이 과정이 반복되면서 바구니의 상태가 자연스럽게 유지됩니다.

JS 쇠막대기 스택 레이저 절단과 함께 읽으며 pop의 의미를 비교하세요. 이 문제는 같은 인형을 제거하지만 쇠막대기 문제는 레이저나 막대 끝의 닫는 괄호를 처리하므로, 제거 조건과 누적값은 다릅니다.

스택은 바구니의 마지막 인형을 찾는 데 쓰입니다. 선택한 열에서 인형을 찾기, 원래 칸을 0으로 바꾸기, 제거 개수를 갱신하기는 여전히 별도 로직으로 처리합니다.

내 풀이와 정답 풀이의 차이점

두 구현의 차이는 바구니 자료구조가 아니라 선택한 열을 탐색하고 탐색을 멈추는 방식입니다.

첫 구현은 while과 moveIndex를 쓰고 매 이동 뒤 인덱스를 초기화합니다. 둘째 구현은 이동마다 for를 새로 시작하고 인형 하나를 처리한 뒤 break합니다.

두 구현의 arr와 stack은 모두 바구니를 나타내며 LIFO 규칙을 따릅니다. 배열은 저장 수단이고 스택은 사용하는 규칙이므로 서로 대립하는 선택지가 아닙니다.

또한 내가 푼 풀이에서는 moveIndex와 같은 보조 변수를 사용해 상태를 직접 관리해야 했지만, 정답 풀이는 반복문과 스택만으로 흐름이 자연스럽게 이어집니다. 이는 코드의 가독성과 유지보수 측면에서도 큰 차이를 만듭니다.

코딩 테스트에서는 정답 여부뿐만 아니라, 문제를 얼마나 구조적으로 이해했는지도 함께 평가됩니다. 같은 문제를 풀더라도, 문제의 핵심 개념을 정확히 짚어낸 풀이가 더 좋은 코드로 평가받게 됩니다.

결론

크레인 인형뽑기 문제는 구현 문제처럼 보이지만, 실제로는 스택의 개념을 정확히 이해하고 있는지를 묻는 문제입니다. 문제의 규칙을 그대로 자료구조로 옮길 수 있을 때, 코드도 자연스럽고 간결해집니다.

FAQ

Q. 배열로 바구니를 표현할 때 왜 스택 규칙을 쓰나요?
바구니의 맨 위 인형과 새로 들어온 인형을 비교해야 하기 때문에, 마지막 상태를 바로 확인할 수 있는 스택 구조가 적합합니다.

Q. board 배열을 직접 수정해도 괜찮나요?
문제 조건상 인형이 사라진 자리를 0으로 처리해야 하므로, board를 직접 수정하는 방식이 오히려 직관적입니다.

이 문제를 푼 뒤 확인하기

빈 칸만 있는 열을 고른 경우와 같은 인형 두 개가 연속으로 만나는 경우를 확인해 보세요.

본문의 예제와 이 반례를 직접 실행해 예상 출력과 비교하고, 결과가 다르면 입력 조건과 반복 범위를 다시 확인하세요.

풀이 함수를 독립 실행해 확인합니다

선수 지식은 배열·조건문·반복문과 위에서 설명한 자료구조입니다. 정답 풀이의 완성 함수 블록 하나를 solution.cjs로 저장하고, 그 아래에 다음 호출을 붙입니다. 앞의 비교용 풀이와 같은 이름의 함수를 함께 붙이지 않습니다. 터미널에서 node solution.cjs로 실행합니다.

console.log(JSON.stringify(solution(...[[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], [1]])));

이 변형 호출의 기대 JSON 출력은 0입니다. 기존 함수 블록의 console.log가 있으면 그 출력 다음 줄에서 확인합니다.

실행 뒤 board가 바뀝니다. 두 풀이를 비교할 때는 행까지 복사한 별도 배열을 전달하세요. 열 높이 N, 이동 수 M에 대해 O(N×M) 시간, 바구니는 최대 O(M) 공간을 사용합니다.

글 소개·학습 준비·전체 목차

주요 포인트 한눈에 보기

선택한 열에서 맨 위 인형 하나를 꺼내 바구니의 마지막 인형과 비교합니다. 같으면 두 인형을 제거하고 답에 2를 더하며, 다르면 바구니에 쌓습니다. 두 풀이 모두 배열을 스택으로 사용하고 열 탐색 방식이 다릅니다.

이 글이 도움이 되었나요?

조회 중

이어서 공부하기

체크포인트: 해설을 가리고 다시 풀고, 경계 입력을 하나 바꾸어 예상 결과와 실제 결과를 비교하세요. 삽입·제거 전후의 상태를 그리고, 내장 도구를 쓰는 풀이와 직접 구현의 목적을 구분하세요.

다음 추천 · 문제: 코딩테스트 스택 풀이: 후위식 연산 계산하기

같은 주제의 문제에 적용하며 방금 배운 개념을 확인합니다.

권장 순서는 학습 가이드와 같습니다. 다른 순서는 접힌 참고 목록에서 확인할 수 있습니다. 읽음 표시는 숙달이나 채점 결과가 아닙니다.

원래 과정 목록 순서 (선택)

읽음 기록

읽음 — / 전체 58개 · 필수 58개

이 글을 쓴 해비

웹사이트를 제작·운영하고, 개인 서비스와 자동화·AI 도구를 만듭니다. 직접 만들며 배운 내용과 문제 해결 과정을 BlogFlow에 기록합니다.

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기