티스토리 뷰

반응형

 

프로그래머스 - 크레인 인형 뽑기 게임

 

이 문제는 예외처리를 하느라 푸는데 시간이 오래 걸렸다. 프로그래머스는 제출할 때 여러 테스트 케이스로 검사하는데 이 문제는 테스트 케이스가 17개였고,  

처음에는 단 한 개만 통과되서 한참 고민하다가 예외처리 하나를 해줬더니

두 번째에는 1-2 테스트 케이스가 통과되었고 나머지들은 또 다른 예외처리를 해주었더니 한번에 통과가 되었다. 

 

프로그래머스는 보안상 문제로 테스트케이스를 공개하지 않는다고 한다. 진짜 뭔지 알려주기라도 하면 어디가 잘못됐는지 알았을 텐데 모르니까 미칠뻔했다.. 오늘은 예외처리의 중요성을 뼈저리게 느끼게 되었기 때문에 풀이를 하며 내가 처음에 놓쳤던 조건들을 짚고 넘어가 볼 것이다. 

 

 


 

풀이과정 & 예외처리

 

이 문제는 이차원 배열을 사용해서 인형뽑기를 하는 것이었는데 포인트는 위에서 세로줄 한 줄씩 뽑는 거기 때문에, 재 배열을 시켜주는 게 필요하다고 생각했다. 

아래의 형태의 배열이 있고 위에서부터 뽑는다면 

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

 

이런식으로 세로 한 줄을 가로로 바꿔주는 과정이 있어야 풀기 편하다고 생각했다. 

 

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

 

 

놓쳤던 예외처리 1

이 때 처음에 2차 배열이 5 x 5에서 30 x 30까지 있다고 했는데 나는 5x5인 경우만 생각해 특정 테스트 케이스에 통과하지 못했다. 

처음 작성한 코드인데 이렇게되면 당연히 5 x5의 배열만 만들게 되므로 board가 그 이상일 경우에는 오류가 난다. 

 

  let newBoard = Array.from(Array(board.length), () => Array());
   
  for (let i = 0; i < board.length; i++) {
    newBoard[0].push(board[i][0]);
    newBoard[1].push(board[i][1]);
    newBoard[2].push(board[i][2]);
    newBoard[3].push(board[i][3]);
    newBoard[4].push(board[i][4]);
  }

 

 

따라서 나는 2중 반복문을 써서 주어진 보드 길이만큼 새로운 보드가 생성될 수 있도록 했다. 

반복문 2개 쓰는게 안 좋다는데 나는 다른 방법이 생각나지 않았다..

let newBoard = Array.from(Array(board.length), () => Array());
   
 for (let i = 0; i < board.length; i++) {
    for (let j = 0; j < board.length; j++) {
      newBoard[j].push(board[i][j]);
    }
  }

 

 

 

newBoard의 모든 배열의 0 제거 

만약 인형이 없는 곳에서 크레인을 작동시키는 경우에는 아무런 일도 일어나지 않습니다. 

 

위의 조건을 따로 처리해주기 번거로울 것 같아서 새로 만든 배열에서 0을 제거하면 좋겠다고 생각했다. 

이 또한 반복문을 통해 0이 아닌 값들만 true로 새 배열을 만들어 리턴하는 filter() 메서드를 이용했다. 

 

  let filterBoard = [];

  for (let i = 0; i < board.length; i++) {
    filterBoard.push(newBoard[i].filter((v) => v !== 0));
  }

 

newBoard에서 0을 모두 제거한 후의 output이다 .

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

 

 

 

스택 & 큐의 개념을 이용한 반복문 

문제를 보면 스택과 큐의 개념을 활용한 문제라고 생각이 들었는데 그래서 push(), shift(), pop()을 적절히 활용했다. 

 

1. 인형을 뽑을 때는 filterBoard의 각 index 배열의 첫 번째 원소가 빠지므로 shift()를 활용했다.

2. latestPick과 newPick이라는 변수를 활용해 bucket에 뽑은 인형들을 담으며 값을 비교하고

둘의 값이 같으면 count++ 를 해준 뒤 bucket의 가장 마지막에 들어있는 인형을(latestPick) pop()을 해주고 다르면 bucket에 newPick값을 push()해주었다. 

 

이런 식으로 하고 최종적으로 중복된 값이기 때문에 count *2 값을 리턴해주었다. (위 반복문에서 count+=2 해줘도 상관없다.)

 

다만 여기서 예외처리를 놓쳤던 부분이 두 가지가 있었다. 

 

 

 

놓쳤던 예외처리 2

 

moves의 길이는 최소 1이기 때문에 아무것도 없을 경우를 생각하지는 않아도 된다. 

하지만 bucket의 길이가 0일 경우에만 처음 값을 push 해주고, 다른 경우까지는 생각하지 않았기 때문에 초기에 나는 

반복문이 돌기전 아래와 같이 미리 moves의 첫 번째 값에 해당하는 인형을 bucket에 넣어줬었다. 

 bucket.push(filterBoard[moves[0] - 1].shift());

 

하지만 연속으로 같은 인형이 뽑힐 경우를 놓쳤었다. (이 부분이 아마 테스트 케이스 1, 2번에 해당하는 경우였을 것이다.)

예를 들어 아래의 인형 뽑기에서 moves가 [1,4,1,4] 일 때 뽑히는 인형은 [4,4,3,3] 이므로 bucket.length가 0이 되는 순간이 다시 존재하게 된다. (4를 만나 없어지고, 빈 배열이 됨)

 

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

 

따라서 반복문 밖이 아닌 안에서 아래의 조건을 추가해주었다. 

 

 if (bucket.length === 0) {
      bucket.push(filterBoard[moves[i] - 1].shift());
    }

 

 

 

 

놓쳤던 예외처리 3

또 특정 테스트케이스가 통과되지않아 가만히 들여다보던중 인형 뽑기 줄에 인형이 하나도 없는 경우를 생각하지 않았다는 것을 깨달았다. 

예를 들어 이렇게 첫 번째 줄에 인형이 하나도 없는 경우가 생기고 moves는 [1, 1]이라고 했을 때 bucket에는 이전에 0을 다 제거했으므로 [undefined, undefined]가 담길 것이다. 

 

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

 

하지만 latestPick과 newPick이 undefined로 값이 같으니 count++이 되기 때문에 이를 방지해줘야 한다..

따라서 아래와 같이 undefined가 아닐 경우만 실행되도록 추가해주었다. 

   if (latestPick === newPick && newPick !== undefined) {
        count++;
        bucket.pop();
      } else {
        bucket.push(newPick);
      }

 

결국 이렇게 해서 통과는 했는데.. 테스트 케이스가 공개되지 않는 탓에 문제를 열 번도 더 읽어보는 수고를 했다.. 그리고 도움말 페이지에서 나와 같은 번호의 테스트 케이스를 통과하지 못한 사람들이 올린 글을 보고 어떤 예외처리가 부족했는지 정보를 얻기도 했다. 

 


 

전체 답안

 

function solution(board, moves) {
  let count = 0;
  let bucket = [];

  let newBoard = Array.from(Array(board.length), () => Array());

  for (let i = 0; i < board.length; i++) {
    for (let j = 0; j < board.length; j++) {
      newBoard[j].push(board[i][j]);
    }
  }

  //모든 배열  0 제거
  let filterBoard = [];

  for (let i = 0; i < board.length; i++) {
    filterBoard.push(newBoard[i].filter((v) => v !== 0));
  }

  for (let i = 0; i < moves.length; i++) {
    if (bucket.length === 0) {
      bucket.push(filterBoard[moves[i] - 1].shift());
    } else {

      let latestPick = bucket[bucket.length - 1];
      let newPick = filterBoard[moves[i] - 1].shift();

      if (latestPick === newPick && newPick !== undefined) {
        count++;
        bucket.pop();
      } else {
        bucket.push(newPick);
      }
    }
  }
  return count * 2;
}
반응형