프로그래머스: 아이템 줍기⭐⭐⭐

업데이트:     Updated:

카테고리:

태그: , , ,

체감 난이도 : ⭐⭐⭐
유형 : DFS/BFS
걸린 시간: 1시간 초과!

🧐 문제

문제 설명

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

rect_1.png

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

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

rect_2.png

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

rect_4.png

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

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

rect_3.png

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

rect_7.png

지형을 나타내는 직사각형이 담긴 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

rect_5.png

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

입출력 예 #2

rect_6.png

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

입출력 예 #3

rect_8.png

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

입출력 예 #4, #5

설명 생략


✏️ 풀이 과정

먼저 테이블을 생성하고, 각 직사각형을 배치해야 한다고 생각했다.

이때, 직사각형의 테두리만 1로 두고, 내부와 외부는 비어있도록 두어야 한다.

하지만, 이렇게만 해서는 문제가 있다.

1 1 1
1 0 0
1 1 1

위와 같이 꺾여가야할 부분이,

1 1
1 1

뭉쳐져서 구분할 수가 없어지기 때문이다.

이를 위해선, 해상도를 늘리는 것처럼 전체 좌표를 2배를 늘려야한다.

이후, DFS/BFS를 사용하여 테두리를 돌고, 둘레/2를 해주면 된다.

이 문제에서는 DFS를 사용해도 최소 거리를 구할 수 있기에,
편한 DFS를 사용하였다.


✏️ 나의 풀이

#include <bits/stdc++.h>

using namespace std;

// 테이블
vector<vector<int>> table(102, vector<int>(102,-1));
// 방문 벡터
vector<vector<bool>> visited(102, vector<bool>(102,false));
int minDist = INT_MAX;
vector<vector<int>> xy = {{1,0}, {0,1}, {-1,0}, {0,-1}};

// 테이블에 중앙이 0으로 채워진 직사각형을 배치한다.
void make_table(vector<int> rectangle) {
    
    int px1 = rectangle[0]*2, py1 = rectangle[1]*2;
    int px2 = rectangle[2]*2, py2 = rectangle[3]*2;
    
    for(int i=px1; i<=px2; i++) {
        for(int j=py1; j<=py2; j++) {

            // 직사각형 내부라면
            if(px1<i && i<px2 && py1<j && j<py2) {
                table[i][j] = 0;
            }
            // 직사각형 테두리라면
            else {
                // 다른 직사각형의 내부인 경우, 0
                // 아니라면, 1
                if(table[i][j]==0) table[i][j] = 0;
                else table[i][j] = 1;
            }
        }
    }
}

void dfs(vector<int> cur, vector<int> dst, int dist) {
    int cx = cur[0], cy = cur[1];
    
    if(table[cx][cy]<=0 || visited[cx][cy]) return;
    if(cur == dst) {
        minDist = min(dist, minDist);
        return;
    }
    
    // 백트래킹을 사용하여 돌아준다.
    for(int i=0; i<4; i++) {
        vector<int> next = {cur[0]+xy[i][0], cur[1]+xy[i][1]};
        
        visited[cur[0]][cur[1]] = true;
        dfs(next, dst, dist+1);
        visited[cur[0]][cur[1]] = false;
    }
    
}

int solution(vector<vector<int>> rectangle, int characterX, int characterY, int itemX, int itemY) {
    
    for(auto r : rectangle) {
        make_table(r);
    }
    
    // 전체 좌표를 2배하여, 해상도를 늘려준다.
    dfs({characterX*2,characterY*2}, {itemX*2,itemY*2}, 0);
    
    return minDist/2;
}

✔️ 다른 사람의 풀이

#include <string>
#include <vector>
#include <iostream>
#include <queue>
using namespace std;

// 동 서 남 북
int dy[] = {0, 0, 1, -1};
int dx[] = {1, -1, 0, 0};

int solution(vector<vector<int>> rectangle, int characterX, int characterY, int itemX, int itemY)
{
    int answer = 0;
    
    // 1. 캐릭터 좌표, 아이템 좌표 모두 2배로 늘린다.
    characterX *= 2, characterY *= 2, itemX *= 2, itemY *= 2;
    // board: 직사각형을 표현할 배열이다.(문제에 직사각형을 나타내는 모든 좌표값은 50이하라고 했지만, 
    // 우리는 모든 좌표값에 대해 2배를 할 것이므로 50*2=100이므로 좌표값은 최대 100이 될 것이다.)
    vector<vector<int>> board(110, vector<int>(110));
    
    // 직사각형의 시작점과 끝점
    int x1, y1, x2, y2;
    
    // 2. 직사각형을 board 배열에 모두 입력해준다.
    for (int i = 0; i < rectangle.size(); i++)
    {
        for (int j = 0; j < rectangle[0].size(); j++)
            rectangle[i][j] = rectangle[i][j] * 2;

        x1 = rectangle[i][0], y1 = rectangle[i][1];
        x2 = rectangle[i][2], y2 = rectangle[i][3];

        for (int y = y1; y <= y2; y++)
            for (int x = x1; x <= x2; x++)
                board[y][x] = 1;
    }

    // 3. 직사각형의 내부는 모두 0으로 채워준다.
    for (int i = 0; i < rectangle.size(); i++)
    {
        x1 = rectangle[i][0], y1 = rectangle[i][1];
        x2 = rectangle[i][2], y2 = rectangle[i][3];

        for (int y = y1 + 1; y < y2; y++)
            for (int x = x1 + 1; x < x2; x++)
                board[y][x] = 0;
    }

    // 4. BFS
    queue<pair<int, int>> q;
    q.emplace(characterY, characterX);
    while (!q.empty())
    {
        int y = q.front().first;
        int x = q.front().second;
        q.pop();

        if (y == itemY && x == itemX)
            break;

        for (int i = 0; i < 4; i++)
        {
            int nextY = y + dy[i];
            int nextX = x + dx[i];

            if (board[nextY][nextX] == 1)
            {
                q.emplace(nextY, nextX);
                board[nextY][nextX] = board[y][x] + 1;
            }
        }
    }

    // 좌표를 모두 2배로 늘려서 계산했으니 answer에는 2로 나눈 아이템의 좌표값을 넣어주면 정답이다.
    answer = board[itemY][itemX] / 2;
    return answer;
}

int main()
{
    vector<vector<int>> rectangle = {{1, 1, 7, 4}, {3, 2, 5, 5}, {4, 3, 6, 9}, {2, 6, 8, 8}};
    int characterX = 1;
    int characterY = 3;
    int itemX = 7;
    int itemY = 8;

    solution(rectangle, characterX, characterY, itemX, itemY);
}

🧐 분석

  • 직사각형을 순회하며 테이블을 1로 채우고,
    다시 한번 더 순회하며 내부를 0으로 채워주고 있다.
  • BFS를 사용하고 있다.

🪶 후기

다음에는 DFS가 아닌, BFS로 풀수 있다면 풀어봐야겠다.

Programmers 카테고리 내 다른 글 보러가기

댓글남기기