문제 설명
다음과 같은 다각형 모양 지형에서 캐릭터가 아이템을 줍기 위해 이동하려 합니다.
지형은 각 변이 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 이하인 자연수입니다.
- 지형을 나타내는 다각형 테두리 위의 한 점이 주어집니다.
- 캐릭터와 아이템의 처음 위치가 같은 경우는 없습니다.
풀이
문제를 요약하면 다음과 같다.
여러 사각형이 겹쳐진 상태에서 출발점부터 목적지까지 최단 경로를 찾는 것이다.
단, 겹쳐진 도형의 테두리만 이용해야 한다.
간단해 보이지만, 처리해야 할 것이 조금 있다.
우선, 큰 그림을 그려보면 탐색할 수 있는 map을 그리는 것이 우선이다.
즉, 주어진 사각형들의 테두리만 연결하여 하나의 도형으로 만드는 것이 첫 번째로 할 일이다.
다양한 방법이 있겠지만, 하나의 도형의 테두리가 다른 도형 안에 있는지 체크하는 방법을 사용했다.
즉, 네 변의 한 점마다 다른 도형 안에 있는 점인지 확인하면서 map을 채웠다.
bool IsInner(int x, int y, vector<vector<int>>& rectangle, int n)
{
bool bResult = false;
for(int i = 0; i < rectangle.size(); i++)
{
if(i == n) continue;
if((rectangle[i][0]*2 < x && x < rectangle[i][2]*2) && (rectangle[i][1]*2 < y && y < rectangle[i][3]*2))
{
bResult = true;
break;
}
}
return bResult;
}
코드를 보면 하나의 좌표의 2를 곱한 것을 볼 수 있다.
이점이 두 번째로 처리해야 하는 작업이다.
일반적인 좌표로 해당 문제를 처리하면, 떨어져 있는 도형인데 표현상 붙어 있게 표시가 된다.
{3, 5}와 {3,6}은 떨어져 있지만, map으로 표시하면 붙어있는 것으로 처리된다.
따라서, 모든 좌표에 2씩 곱하여 2배로 큰 좌표로 설정하는 것이다.
쉽게 말해, 한 칸에 0.5씩이라고 생각하면 된다.
이렇게 map을 만들고 bfs를 진행한 뒤, 2로 나누어 결과를 반환하면 된다.
전체 코드
#include <string>
#include <vector>
#include <queue>
#include <iostream>
using namespace std;
bool IsInner(int x, int y, vector<vector<int>>& rectangle, int n)
{
bool bResult = false;
for(int i = 0; i < rectangle.size(); i++)
{
if(i == n) continue;
if((rectangle[i][0]*2 < x && x < rectangle[i][2]*2) && (rectangle[i][1]*2 < y && y < rectangle[i][3]*2))
{
bResult = true;
break;
}
}
return bResult;
}
int BFS(vector<vector<int>>& map, int characterX, int characterY, int itemX, int itemY)
{
vector<vector<bool>> visited(map.size(), vector<bool>(map[0].size(), false));
queue<pair<int,int>> q;
q.push({characterY*2, characterX*2});
visited[characterY*2][characterX*2] = true;
vector<int> dy = {1, 0, -1, 0};
vector<int> dx = {0, -1, 0, 1};
int result = 0;
while(!q.empty())
{
int size = q.size();
for(int n = 0; n < size; n++)
{
auto [y, x] = q.front();
q.pop();
for(int i = 0; i < 4; i++)
{
int new_y = y + dy[i];
int new_x = x + dx[i];
if(visited[new_y][new_x]) continue;
if(map[new_y][new_x] == 0) continue;
if(new_y == itemY*2 && new_x == itemX*2) return (++result)/2;
q.push({new_y, new_x});
visited[new_y][new_x] = true;
}
}
result++;
}
}
int solution(vector<vector<int>> rectangle, int characterX, int characterY, int itemX, int itemY) {
int answer = 0;
const int MAX = 102;
vector<vector<int>> map(MAX, vector<int>(MAX, 0));
for(int i = 0; i < rectangle.size(); i++)
{
int startX = rectangle[i][0]*2;
int startY = rectangle[i][1]*2;
int endX = rectangle[i][2]*2;
int endY = rectangle[i][3]*2;
for(int x = startX; x <= endX; x++)
{
if(!IsInner(x, startY, rectangle, i))
{
map[startY][x] = 1;
}
}
for(int x = startX; x <= endX; x++)
{
if(!IsInner(x, endY, rectangle, i))
{
map[endY][x] = 1;
}
}
for(int y = startY; y <= endY; y++)
{
if(!IsInner(startX, y, rectangle, i))
{
map[y][startX] = 1;
}
}
for(int y = startY; y <= endY; y++)
{
if(!IsInner(endX, y, rectangle, i))
{
map[y][endX] = 1;
}
}
}
answer = BFS(map, characterX, characterY, itemX, itemY);
return answer;
}