FrontEnd/프로그래머스

[JS] 퍼즐조각 채우기

728x90

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

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

 

정말 쉽지 않은 문제였다.

 

문제 자체를 접근하는것과 풀이는 처음에 잘했었다.

 

1. table에서 블럭들의 정보를 배열로담아옴

2. 회전이 가능하므로 회전가능한 블럭들의 정보들을 배열에 추가

3. 완전탐색을 활용하여 game_board에 빈칸이 있는지 탐색

 

 

따라서 아래와 같이 해결해 봤는데 아무리 생각을 해도 시간초과를 해결할 수 가 없었다.

function solution(game_board, table) {
  const dc = [1, 0, -1, 0];
  const dr = [0, 1, 0, -1];
  const len = table.length;

  //퍼즐조각들의 정보를 가져옴
  const getPuzzle = (table) => {
    const visited = [...Array(len)].map((v) => Array(len).fill(false));

    const dfs = (cood, relativeCood) => {
      const [r, c] = cood;
      const [rR, rC] = relativeCood;
      if (visited[r][c]) return [];
      visited[r][c] = true;
      const ret = [[rR, rC]];
      for (let d = 0; d < 4; d++) {
        const [nR, nC] = [r + dr[d], c + dc[d]];
        const [nRR, nRC] = [rR + dr[d], rC + dc[d]];
        if (0 <= nR && nR < len && 0 <= nC && nC < len && table[nR][nC])
          ret.push(...dfs([nR, nC], [nRR, nRC]));
      }
      return ret;
    };
    const ret = [];
    for (let i = 0; i < len; i++) {
      for (let j = 0; j < len; j++) {
        if (table[i][j] && !visited[i][j]) ret.push(dfs([i, j], [0, 0]));
      }
    }
    return ret;
  };

  //좌표를 우측으로 회전
  const lotateCoodRight = ([r, c]) => {
    return [c, -r];
  };

  //회전한 4가지 piece를 가져옴
  const lotatePiece = (piece) => {
    const ret = [piece];
    for (let i = 0; i < 3; i++) {
      const tmp = [];
      for (const cood of ret[ret.length - 1]) {
        tmp.push(lotateCoodRight(cood));
      }
      ret.push(tmp);
    }
    return ret;
  };

  let pieces = [];
  for (const i of getPuzzle(table)) {
    pieces.push(lotatePiece(i));
  }

  const canPutPiece = (piece, r, c) => {
    for (const [dR, dC] of piece) {
      const [nR, nC] = [r + dR, c + dC];
      if (0 <= nR && nR < len && 0 <= nC && nC < len && !game_board[nR][nC])
        continue;
      return false;
    }

    return true;
  };

  const isNearEmpty = (piece, r, c) => {
    for (const [dR, dC] of piece) {
      const [nR, nC] = [r + dR, c + dC];
      for (let i = 0; i < 4; i++) {
        const [nnR, nnC] = [nR + dr[i], nC + dc[i]];
        if (
          0 <= nnR &&
          nnR < len &&
          0 <= nnC &&
          nnC < len &&
          !game_board[nnR][nnC]
        )
          return true;
      }
    }

    return false;
  };

  //보드를바꾸는 함수 (제거하거나 조각을 넣거나)
  const changeBoard = (piece, r, c, isPut) => {
    for (const [dR, dC] of piece) {
      const [nR, nC] = [r + dR, c + dC];
      game_board[nR][nC] = isPut ? 2 : 0;
    }
  };

  let ret = 0;

  const dfs = (cR, cC, cnt, pieces) => {
    if (cR >= len) return;
    if (cnt > ret) ret = cnt;

    const [nR, nC] = [cR + ~~((cC + 1) / len), (cC + 1) % len];
    for (let idx = 0; idx < pieces.length; idx++) {
      const curPiece = pieces[idx];
      for (const piece of curPiece) {
        if (canPutPiece(piece, cR, cC)) {
          changeBoard(piece, cR, cC, true);
          const removePiece = pieces.filter((v, i) => i !== idx);
          if (!isNearEmpty(piece, cR, cC))
            dfs(nR, nC, cnt + piece.length, removePiece);
          changeBoard(piece, cR, cC, false);
        }
      }
    }
    dfs(nR, nC, cnt, pieces);
  };

  dfs(0, 0, 0, pieces);
  return ret;
}

 

 

중요한건 문제에 정해진 조건이었다.

 

이때 문제조건중에서 퍼즐모양을 보드에 놓았을때 주변에 빈칸이 있으면 안된다는 조건이 있었다...

만약 퍼즐을 넣을 수 있는 모든 경우의 수를 구하는 문제였다면 (50*50) 크기의 큰 보드를 주지 않았을텐데 이부분을 간과한 것 같다.

 

 

즉, table에서 추출한 퍼즐 블록들과 game_board에서 추출한 퍼즐 블록들을 비교하면 되는 문제였다.

(사실 포기하고 과정만 블로그에 정리하다가 생각이 떠올라서 수정했다.)

 

 

 

 

 

 

728x90

'FrontEnd > 프로그래머스' 카테고리의 다른 글

[JS] 석유 시추  (1) 2023.12.01
[JS] 붕대 감기  (0) 2023.11.27
[JS] 금과 은 운반하기  (1) 2023.11.13
[JS] 아이템 줍기  (1) 2023.11.06
[JS] 양과 늑대  (2) 2023.11.05