본문 바로가기
코딩테스트 및 개인공부/문제풀이

[프로그래머스 - level3] [DFS/BFS] 아이템 줍기

by JJPearl 2022. 7. 14.
반응형

문제 설명

다음과 같은 다각형 모양 지형에서 캐릭터가 아이템을 줍기 위해 이동하려 합니다.

지형은 각 변이 x축, y축과 평행한 직사각형이 겹쳐진 형태로 표현하며, 캐릭터는 이 다각형의 둘레(굵은 선)를 따라서 이동합니다.

만약 직사각형을 겹친 후 다음과 같이 중앙에 빈 공간이 생기는 경우, 다각형의 가장 바깥쪽 테두리가 캐릭터의 이동 경로가 됩니다.

단, 서로 다른 두 직사각형의 x축 좌표 또는 y축 좌표가 같은 경우는 없습니다.

즉, 위 그림처럼 서로 다른 두 직사각형이 꼭짓점에서 만나거나, 변이 겹치는 경우 등은 없습니다.

다음 그림과 같이 지형이 2개 이상으로 분리된 경우도 없습니다.

한 직사각형이 다른 직사각형 안에 완전히 포함되는 경우 또한 없습니다.

지형을 나타내는 직사각형이 담긴 2차원 배열 rectangle, 초기 캐릭터의 위치 characterX, characterY, 아이템의 위치 itemX, itemY가 solution 함수의 매개변수로 주어질 때, 캐릭터가 아이템을 줍기 위해 이동해야 하는 가장 짧은 거리를 return 하도록 solution 함수를 완성해주세요.

제한사항
  • rectangle의 세로(행) 길이는 1 이상 4 이하입니다.
  • rectangle의 원소는 각 직사각형의 [좌측 하단 x, 좌측 하단 y, 우측 상단 x, 우측 상단 y] 좌표 형태입니다.
    • 직사각형을 나타내는 모든 좌표값은 1 이상 50 이하인 자연수입니다.
    • 서로 다른 두 직사각형의 x축 좌표, 혹은 y축 좌표가 같은 경우는 없습니다.
    • 문제에 주어진 조건에 맞는 직사각형만 입력으로 주어집니다.
  • charcterX, charcterY는 1 이상 50 이하인 자연수입니다.
    • 지형을 나타내는 다각형 테두리 위의 한 점이 주어집니다.
  • itemX, itemY는 1 이상 50 이하인 자연수입니다.
    • 지형을 나타내는 다각형 테두리 위의 한 점이 주어집니다.
  • 캐릭터와 아이템의 처음 위치가 같은 경우는 없습니다.

  • 전체 배점의 50%는 직사각형이 1개인 경우입니다.
  • 전체 배점의 25%는 직사각형이 2개인 경우입니다.
  • 전체 배점의 25%는 직사각형이 3개 또는 4개인 경우입니다.

 

입출력 예
rectangle characterX characterY itemX itemY result
[[1,1,7,4],[3,2,5,5],[4,3,6,9],[2,6,8,8]] 1 3 7 8 17
[[1,1,8,4],[2,2,4,9],[3,6,9,8],[6,3,7,7]] 9 7 6 1 11
[[1,1,5,7]] 1 1 4 7 9
[[2,1,7,5],[6,4,10,10]] 3 1 7 10 15
[[2,2,5,5],[1,3,6,4],[3,1,4,6]] 1 4 6 3 10
입출력 예 설명

입출력 예 #1

캐릭터 위치는 (1, 3)이며, 아이템 위치는 (7, 8)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.

입출력 예 #2

캐릭터 위치는 (9, 7)이며, 아이템 위치는 (6, 1)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.

입출력 예 #3

캐릭터 위치는 (1, 1)이며, 아이템 위치는 (4, 7)입니다. 위 그림과 같이 굵은 선을 따라 이동하는 경로가 가장 짧습니다.

입출력 예 #4, #5

설명 생략


 

풀이 과정

가장 짧은거리 -> BFS 사용

직사각형을 나타내는 모든 좌표값은 1이상 50이하인 자연수 이므로 행,열이 최대값+1 크기인 2차원 board 배열을 선언.

-1로 초기화된 거리를 저장하는 dist 배열을 선언. BFS로 해당 위치를 방문하면 dist 배열의 값을 이전 위치의 값보다 +1 된 값으로 set 해준다.

참고한 블로그:

https://taehoung0102.tistory.com/95

 

[프로그래머스,Java] Levle3: 위클리챌린지 11주차[아이템 줍기]

https://programmers.co.kr/learn/courses/30/lessons/87694#qna 코딩테스트 연습 - 11주차 [[1,1,7,4],[3,2,5,5],[4,3,6,9],[2,6,8,8]] 1 3 7 8 17 [[1,1,8,4],[2,2,4,9],[3,6,9,8],[6,3,7,7]] 9 7 6 1 11 [[2,2,..

taehoung0102.tistory.com

 

board 배열에 사각형 벡터를 이용해 미리 갈 수 있는 길, 없는 길을 채워놓는다.

좌측하단 X~우측상단X 까지, 좌측하단 Y~우측상단Y 까지 테두리 포함해서 1로 채우고(갈 수 있는 길),

테두리 제외해서 0으로 채우면 테두리만 갈 수 있는 길로 남는다.

그런데 해당 방식으로 갈 수 있는 길을 1로 채우면  좌표상으로는 1이 차이나는 칸의 사이를 0으로 채울 수 없다.  

->

이걸 해결하기 위해서 그림을 2배 확대 시키면 된다. (막혀서 이 부분을 블로그에서 참고했다.)

=> board 배열과 dist배열의 크기를 2배로 늘려준다. 배열에 들어갈 좌표값들도 모두 2배.

=> 나중에 answer은 dist배열의 도착칸에 있는 값을 1/2 해주면 정답.

 

 

 

-> 로직을 이대로 했는데 실패하길래 왜인가 했더니 

거리를 저장하는 2차원 배열인 dist의 인덱스에 시작좌표(CharacterX,CharacterY)와 도착점(ItemX,ItemY)을 반대로 넣은 것이었다.

dist[x][y] 에서 x는 행, y는 열을 나타내므로 해당 인덱스에 접근해서 값을 쓰려면 dist[CharacterY][CharacterY] 이렇게 써야한다.

배열에 값을 넣을 때 은근 많이 하는 실수이니 주의하자.

코드

#include <string>
#include <vector>
#include <queue>
// 가장 짧은거리 -> BFS

#define X first
#define Y second
#define MAXSIZE 102 // 51*2 두 배
int dx[4] = {-1,0,1,0};
int dy[4] = {0,-1,0,1};

int board[MAXSIZE][MAXSIZE]{};
int dist[MAXSIZE][MAXSIZE]{};

using namespace std;


int solution(vector<vector<int>> rectangle, int characterX, int characterY, int itemX, int itemY) {
    int answer = 0;
    int rectVecSize = rectangle.size();
    int start_X = characterX*2;
    int start_Y = characterY*2;
    int end_X = itemX*2;
    int end_Y = itemY*2;
 
    // 1.테두리를 포함한 사각형을 1로 채운다
    for(auto rect : rectangle)
    {
        // [좌측 하단 x, 좌측 하단 y, 우측 상단 x, 우측 상단 y]
        for(int i=rect[1]*2;i<=rect[3]*2;++i)
        {
            for(int j=rect[0]*2;j<=rect[2]*2;++j)
                board[i][j] = 1;
        }
    }
     // 2.테두리를 제외한 사각형을 0으로 채운다
     for(auto rect : rectangle)
    {
        // 좌표 = board의 인덱스
        for(int i=rect[1]*2+1;i<rect[3]*2;++i)
        {
            for(int j=rect[0]*2+1;j<rect[2]*2;++j)
            {
                board[i][j] = 0;
            }
        }
    }
  
    for(int i=0;i<MAXSIZE;++i)
    {
        for(int j=0;j<MAXSIZE;++j)
            dist[i][j] = -1;
    }
    
    queue<pair<int,int>> bfsQ;
    bfsQ.push({start_Y,start_X}); // x좌표 = board 열, y좌표 = board 행
    dist[start_Y][start_X] = 0; // 시작점에 +1
    while(!bfsQ.empty())
    {
        pair<int,int> curPos = bfsQ.front();
        bfsQ.pop();
    
        for(int i=0;i<4;++i)
        {
            int nx = curPos.X + dx[i];
            int ny = curPos.Y + dy[i];
            if(nx <0 || ny<0 || nx>= MAXSIZE || ny >= MAXSIZE) continue;
            if(dist[nx][ny] >=0 || board[nx][ny] == 0) continue;
            bfsQ.push({nx,ny});
            dist[nx][ny] = dist[curPos.X][curPos.Y]+1;
        }
    }
  
    answer = dist[end_Y][end_X]/2;
    return answer;
}

 

 

 

반응형