프로그래머스: Lv1. 크레인 인형뽑기 게임

2020. 12. 6. 22:59알고리즘/프로그래머스

반응형
  • 문제 설명

크레인 인형뽑기 게임

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

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

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

크레인 작동 시 인형이 집어지지 않는 경우는 없으나 만약 인형이 없는 곳에서 크레인을 작동시키는 경우에는 아무런 일도 일어나지 않습니다. 또한 바구니는 모든 인형이 들어갈 수 있을 만큼 충분히 크다고 가정합니다. (그림에서는 화면표시 제약으로 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 배열의 가로 크기 이하인 자연수입니다.

입출력 예

boardmovesresult

[[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] 4

입출력 예에 대한 설명

입출력 예 #1

인형의 처음 상태는 문제에 주어진 예시와 같습니다. 크레인이 [1, 5, 3, 5, 1, 2, 1, 4] 번 위치에서 차례대로 인형을 집어서 바구니에 옮겨 담은 후, 상태는 아래 그림과 같으며 바구니에 담는 과정에서 터트려져 사라진 인형은 4개 입니다.


  • 문제풀이

이차원 배열의 board에서 basket안으로 뽑은 인형을 다시 넣고 그사이에 같은 인형이 있을시 터트린다. 이것을 정리하고 보니 basket으로 stack을 이용하여 문제를 풀어야한다 생각했다.

 

  • Stack

- 스택은 보통 '쌓다'다는 표현을 떠올리며, 후입선출(Last In, First Out)구조로 한쪽 끝에서만 데이타를 넣고 빼는 형식으로 자료구조에서 많이 이용하는 하나의 방식이다.

  • Stack의 선언
import java.util.Stack; 
Stack<Integer> stack = new Stack<>(자료형생략가능); //int형 스택 선언
Stack<String> stack = new Stack<>(); //char형 스택 선언

 

  • Stack의 주요 메서드
Stack<Integer> stack = new Stack<>(); //만약 int형 스택 선언시

stack.push(1);     // stack에 값 1 추가
stack.push(2);     // stack에 값 2 추가
stack.size();      // stack의 크기 출력 : 2
stack.empty();     // stack이 비어있는제 check (비어있다면 true)
stack.contains(1) // stack에 1이 있는지 check (있다면 true)
stack.peek() //stack에 가장 최근에 저장된 값(제일 상단의 값)
//stack.peek()은 스택에 data가 하나도 없을때는 exception을 반환한다.

ex) Stack과 Queue (Stack과 Queue의 자세한 내용은 Java탭에서 확인가능.)

Stack과 Queue.

  • 나의 문제풀이
package lv1;

import java.util.Stack;

public class Kakaochoice {
	public int solution(int[][] board, int[] moves) {
		int answer = 0;
		
		Stack<Integer> basket = new Stack<Integer>();

		for (int i = 0; i < moves.length; i++) { // 세로
			for (int j = 0; j < board.length; j++) { // 가로

				if (board[j][moves[i] - 1] != 0 && !basket.empty()) { //크래인이 뽑으려 하는 인형이 있고, basket이 비어있지 않을때 )

					if (basket.peek() == board[j][moves[i] - 1]) {
						
						board[j][moves[i] - 1] = 0; // board 인형 지우기
						basket.pop(); // basket 맨위 인형 지우기(꺼내기)
						
						answer += 2; // board에서 뽑은 인형과 basket인형 이 같을때 answer에 두개 추가.

					} else if (basket.peek() != board[j][moves[i] - 1]) {
						basket.push(board[j][moves[i] - 1]);
						board[j][moves[i] - 1] = 0;
					}
					break;
					
				} else if (board[j][moves[i] - 1] != 0 && basket.empty()) { //basket이 비어있을때
					basket.push(board[j][moves[i] - 1]);
					board[j][moves[i] - 1] = 0;
					break;
				}

			}
		}
		return answer;
	}
	public static void main(String[] args) {
		
		 Kakaochoice kakao = new Kakaochoice();
	      int[][] 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,2,3,4,5
	      int[] moves = {1,5,3,5,1,2,1,4};
	      System.out.println(kakao.solution(board, moves));

	}

}

- 위 코드를 보면 크게 두가지로 생각해 볼 수 있다. 

  1. 이차원 배열에서 인형들을 크레인으로 선택하는것.
  2. 뽑은(선택한)인형들을 basket안에 넣는것.
  3. 넣은 인형중에 같은 인형이 있으면 터트리고 없으면 그냥 쌓는다.

- 저 세가지 항목에서 각 항목사항마다의 조건과 사용메서드를 생각해 보았다.

먼저 크레인이 moves라는 움직임 배열대로 따라 움직이게 되고 moves의 인덱스를 따라갔을때는 크레인이 5개의 행중 하나를 선택하여 그 행의 열을 검사해야 한다. 이때 주의할 점은 board의 요소가 0일때는 인형이 없는것임으로 통과를 시켜줘야 하고 만약 0이 아닐시에는 인형을 꺼내야한다. 이때! 인형을 꺼내고 다음요소까지 확인하게 된다면 basket안에 남아있는 인형이 모두 다 들어가기 때문에 조건식에서 다음 moves로 이동시켜줄 break문을 걸어야 인형 하나만 뽑고 다음으로 넘어가게 된다. (필자는 break를 걸어주지 않아서 출력했을 시 예상보다 많은 인형의 갯수가 나왔다..)

인형이 선택 되면 그다음 어떤 방식으로 같은것을 찾아주는지 생각을 해봐야 했다.

  • 해당 인형을 크레인이 뽑았을 시 basket에 같은 인형이 존재하면 stack.push()를 하지 않고 <<board의 선택한 인형>><<board의 선택한 인형과 basket의 같은 인형>>의 요소를 삭제해준다.
  • 그리고 answer 값을 2를 증가시켜준다. (이부분에서 착각할 수 있는것은 눈에 보이기엔 인형을 넣고 비교해서 터트리는 거지만 구현에서는 비교만해주고 같으면 push하지 않고 각 요소만 삭제해주며 터지는 갯수를 증가시킨다.)
  • 여기서도 주의사항이 있었는데, basket.peek()을 사용할 때 애초에 basket에 요소가 텅 비어있다면 조건식에서 checking을 할때 exception이 날라오게 되었다. (빈 바구니를 체크하게 되므로)
  • 그러하여 첫번째 조건문에는 바구니의 요소가 있냐, 없냐를 먼저 판단하고 그 다음 분기로 넘어가게 구현했다.
  • 이 부분에서 차라리 처음부터 basket안에 0이라는 값을 넣고 비교를 해준다면 분기를 줄일 수 있다고 생각하였지만 basket안에 넣어야 하지 말아야 될 요소를 넣고 비교하는거라 최후의 수단으로 생각하였다.
  • 이런 과정이 하나의 써클이며 moves의 다음 요소로 넘어갈때는 moves-1요소가 board[][moves-1]요소 이므로 두개의 for문으로만 돌려주면 된다.
  • 다른 문제 풀이

다른풀이를 검색해 본 결과 ArrayList로 구현을 한 분들도 많았다. 효율성으로 따져봤을땐 ArrayList는 값을 추가/삭제에 있어서 충분한 방법이지만, 이 문제에 대해서는 Stack으로 구현시 top의값을 push하고 pop하는데에 있어서 조금더 효율적인것 같다.

반응형