루고 P2885 유성 폭 shower S 문제 해결

문제

해법

이 문제는 제한 조건이 있는 BFS를 사용해야 합니다. 유성이 실시간으로 발생하기 때문에 이를 고려해야 합니다.

두 가지 접근 방법을 소개합니다:

해법 1

이것은 처음 시도했던 방법입니다.

유성을 실시간으로 처리하며, BFS는 시간 순서대로 탐색하므로 유성을 그때그때 생성할 수 있습니다. 유성이 발생하는 시간을 주의해야 합니다. 만약 t초에 유성이 발생하면, 그 초에는 이동할 수 없으므로 매 초마다 유성을 먼저 생성해야 합니다.

어떻게 특정 위치가 안전한지 판단할까요? 두 개의 배열을 사용하여 하나는 실시간 유성을 저장하고 다른 하나는 유성이 발생했는지를 저장합니다. 자세한 내용은 코드를 참조하세요.

구현 시 Bessie가 300을 넘어갈 수 있다는 점도 주의해야 합니다. 300은 단지 유성의 경계일 뿐입니다.

코드 1:

#include <iostream>
#include <queue>
#include <cstring>
#define MAX 401
using namespace std;

int m;
int danger[MAX][MAX];
int meteor[MAX][MAX];
bool visited[MAX][MAX];
int dirX[] = {0, 0, 1, -1};
int dirY[] = {1, -1, 0, 0};

struct Meteor {
    int x, y, time;
} meteors[500000];

bool isValid(int x, int y) {
    return x >= 0 && x <= 400 && y >= 0 && y <= 400;
}

void generateMeteors(int currentTime) {
    for (int i = 1; i <= m; i++) {
        if (meteors[i].time == currentTime) {
            meteor[meteors[i].x][meteors[i].y] = 1;
            for (int j = 0; j < 4; j++) {
                int nx = meteors[i].x + dirX[j];
                int ny = meteors[i].y + dirY[j];
                if (isValid(nx, ny)) meteor[nx][ny] = 1;
            }
        }
    }
}

void bfs() {
    queue<pair<int, pair<int, int>>> q;
    q.push({0, {0, 0}});
    visited[0][0] = true;
    
    while (!q.empty()) {
        auto current = q.front();
        q.pop();
        int currentTime = current.first;
        int cx = current.second.first;
        int cy = current.second.second;
        
        generateMeteors(currentTime);
        
        if (!danger[cx][cy]) {
            cout << currentTime << endl;
            return;
        }
        
        for (int i = 0; i < 4; i++) {
            int nx = cx + dirX[i];
            int ny = cy + dirY[i];
            if (isValid(nx, ny) && !meteor[nx][ny] && !visited[nx][ny]) {
                visited[nx][ny] = true;
                q.push({currentTime + 1, {nx, ny}});
            }
        }
    }
}

int main() {
    memset(visited, false, sizeof(visited));
    memset(meteor, 0, sizeof(meteor));
    
    cin >> m;
    for (int i = 1; i <= m; i++) {
        cin >> meteors[i].x >> meteors[i].y >> meteors[i].time;
        danger[meteors[i].x][meteors[i].y] = 1;
        for (int j = 0; j < 4; j++) {
            int nx = meteors[i].x + dirX[j];
            int ny = meteors[i].y + dirY[j];
            if (isValid(nx, ny)) danger[nx][ny] = 1;
        }
    }
    generateMeteors(0);
    bfs();
    return 0;
}

해법 2

코치가 제공한 아이디어로, 첫 번째 해법보다 간결합니다.

각 지점이 유성에 의해 파괴되는 시간을 기록하는 배열을 만들어 사전 처리한 후 일반적인 BFS를 수행합니다.

사전 처리 중 해당 지점이 더 일찍 유성에 의해 파괴되면 기존 값으로 덮어씁니다.

0초에 유성이 있을 수 있으므로 안전한 지점을 0으로 표시하지 않아야 합니다.

첫 번째 해법에 비해 유성 시간과 유성이 발생했는지 여부를 한 배열로 처리할 수 있어 매우 간결합니다.

코드 2:
#include <iostream>
#include <queue>
#include <cstring>
#define MAX 401
using namespace std;

typedef pair<int, int> Point;
int m;
int destroyTime[MAX][MAX];
int visit[MAX][MAX];
int dirX[] = {0, 0, 1, -1};
int dirY[] = {1, -1, 0, 0};

struct Meteor {
    int x, y, time;
} meteors[500000];

bool isValid(int x, int y) {
    return x >= 0 && x <= 400 && y >= 0 && y <= 400;
}

void bfs() {
    queue<Point> q;
    q.push({0, 0});
    visit[0][0] = 0;
    
    while (!q.empty()) {
        Point current = q.front();
        q.pop();
        
        if (destroyTime[current.first][current.second] == -1) {
            cout << visit[current.first][current.second] << endl;
            return;
        }
        
        for (int i = 0; i < 4; i++) {
            int nx = current.first + dirX[i];
            int ny = current.second + dirY[i];
            if (isValid(nx, ny) && visit[nx][ny] == 0x3f3f3f3f && 
                (visit[current.first][current.second] + 1 < destroyTime[nx][ny] || destroyTime[nx][ny] == -1)) {
                visit[nx][ny] = visit[current.first][current.second] + 1;
                q.push({nx, ny});
            }
        }
    }
}

int main() {
    memset(destroyTime, -1, sizeof(destroyTime));
    memset(visit, 0x3f3f3f3f, sizeof(visit));
    
    cin >> m;
    for (int i = 1; i <= m; i++) {
        cin >> meteors[i].x >> meteors[i].y >> meteors[i].time;
    }
    
    for (int i = 1; i <= m; i++) {
        if (destroyTime[meteors[i].x][meteors[i].y] == -1 || meteors[i].time < destroyTime[meteors[i].x][meteors[i].y])
            destroyTime[meteors[i].x][meteors[i].y] = meteors[i].time;
        for (int j = 0; j < 4; j++) {
            int nx = meteors[i].x + dirX[j];
            int ny = meteors[i].y + dirY[j];
            if (isValid(nx, ny) && (destroyTime[nx][ny] == -1 || meteors[i].time < destroyTime[nx][ny]))
                destroyTime[nx][ny] = meteors[i].time;
        }
    }
    
    bfs();
    return 0;
}

태그: C++ bfs ProblemSolving algorithm

7월 3일 04:12에 게시됨