문제

링크

$N\times N$정사각형 격자판이 있다. 각 칸은 이동가능한 칸이거나 벽이다. 또 $M$명의 택시 승객이 있는데, 각 승객은 현재 위치와 목적지를 갖는다. 택시는 벽이 아닌 상하좌우 칸으로 이동할 수 있으며, 초기 연료의 양도 주어진다.

택시를 운전해서 가장 가까운 승객(여럿이라면 그 중 행/열번호가 가장 작은)을 목적지로 태워주는 것을 반복한다고 하자. 택시는 한 칸 움직일 때마다 연료를 1 소모하며, 어떤 승객을 성공적으로 태워주었다면 승객을 태운 상태로 이동한 거리의 두 배 만큼 연료를 회복한다.

모든 승객을 태워다 줄 수 있는지, 가능하다면 마지막에 남는 연료는 몇인지 구하는 문제이다.

접근

어떤 승객이 존재하는지, 또 존재한다면 목적지는 어디인지 구하기 위하여 map<pair<int,int>,pair<int,int>>을 사용하였다. 그리고 택시가 한 명의 승객을 성공적으로 태우는 부분을 taxi()함수로 구현하고, 이것이 실패할 때까지 무한 루프가 돌도록 하였다.

다음 태울 승객을 구하려면 각 승객까지의 위치를 알아야 한다. 이를 위해 먼저 BFS를 이용하여 현재 택시의 위치를 기준으로 모든 칸까지의 거리를 구하는 void get_all_distance()를 구현하였다. 이 함수를 호출 한 이후에, 승객 정보를 담은 map을 순회하면서 가장 가까운 승객을 찾는다. 태울 승객을 찾았다면 그 승객을 태우고, 목적지로 이동한다. 이렇듯 기본적인 알고리즘은 간단하고, 사이 사이에 어떤 경우에 성공적으로 승객을 운송할 수 없는지 생각하여 처리해주면 된다.

주의할 점

승객을 성공적으로 나를 수 없는 경우를 꼼꼼히 생각해보아야 한다.

  • 벽에 막혀서 다음 승객이 있는 곳까지 갈 수 없는 경우
  • 다음 승객이 있는 곳까지 갈 연료가 없는 경우
  • 비슷하게, 벽에 막혀서 목적지까지 갈 수 없는 경우
  • 연료가 없어서 목적지까지 갈 수 없는 경우

등이 있을 것이다. 나는 80% 언저리에서 틀렸습니다를 한 번 받았는데, 승객이 벽에 막혀서 갈 수 없는 곳을 목적지로 설정한 경우를 제대로 처리해주지 않아서였다.

Code

링크

#include <algorithm>
#include <deque>
#include <iostream>
#include <map>
#include <vector>

using namespace std;

int N, M, F;
int x, y;
int board[20][20] = {0};
int distance_board[20][20];
int MAX_DIST = 1000;
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
map<pair<int, int>, pair<int, int>> customer;

void get_all_distance() {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            distance_board[i][j] = MAX_DIST;
        }
    }
    deque<pair<int, int>> Q;
    Q.push_back(make_pair(x, y));
    distance_board[x][y] = 0;
    while (Q.size() != 0) {
        int row, col;
        tie(row, col) = Q.front();
        Q.pop_front();
        for (int d = 0; d < 4; d++) {
            int nrow = row + dx[d];
            int ncol = col + dy[d];
            int ndist = distance_board[row][col] + 1;
            if (0 <= nrow && nrow < N && 0 <= ncol && ncol < N && board[nrow][ncol] != 1 && ndist < distance_board[nrow][ncol]) {
                distance_board[nrow][ncol] = ndist;
                Q.push_back(make_pair(nrow, ncol));
            }
        }
    }
}

bool taxi() {
    // first, do bfs to find next target customer.
    get_all_distance();
    pair<int, int> nearest_customer;
    int nearest_distance = MAX_DIST;
    for (auto iter : customer) {
        pair<int, int> key = iter.first;
        if (distance_board[key.first][key.second] < nearest_distance) {
            nearest_distance = distance_board[key.first][key.second];
            nearest_customer = key;
        }
    }
    // stop iteration if customer was not found
    if (nearest_distance == MAX_DIST) {
        if (customer.size() != 0) {
            F = -1;
        }
        return false;
    }
    // stop iteration if fuel is low
    if (F < nearest_distance) {
        F = -1;
        return false;
    }
    // move taxi to customer position
    F -= nearest_distance;
    x = nearest_customer.first;
    y = nearest_customer.second;

    // move taxi to goal.
    get_all_distance();
    pair<int, int> goal = customer[nearest_customer];
    x = goal.first;
    y = goal.second;
    int used = distance_board[x][y];
    customer.erase(nearest_customer);
    if (used == MAX_DIST || F < used) {
        F = -1;
        return false;
    }
    // refill fuel twice.
    F += used;
    return true;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    // get input
    cin >> N >> M >> F;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            cin >> board[i][j];
        }
    }
    cin >> x >> y;
    x--, y--;  // zero-based index
    for (int i = 0; i < M; i++) {
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        customer[make_pair(a - 1, b - 1)] = make_pair(c - 1, d - 1);
    }
    while (taxi()) {
    }
    cout << F;
    return 0;
}

여담

이번에는 map을 사용하는 법을 공부해보게 되었다. 사실 꼭 map을 쓸필요는 없는것 같은데, Python에는 없는 구조라 맛을 보았다. 뭐…그냥 신기한 것 같다.