Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cpp] Dijkstra의 탑 6번 - 무서운 시어머니 #53

Merged
merged 4 commits into from
Aug 16, 2023

Conversation

minjae9610
Copy link
Member

@minjae9610 minjae9610 commented Aug 8, 2023

🌁 Background

민코딩 알고리즘 과제를 풀던 중, 해당 문제에서 다익스트라 알고리즘을 이용해 전체 노드에 대한 최솟값을 구한 후 전체 노드를 다시 한번 순회하는 것에 대한 이야기를 하던 중 일반적인 다익스트라 문제의 상황과는 다르게 처음 방문하는 경로가 최단 경로임을 보장할 수 있는 상황이라는 것을 깨달아 올려봅니다.

👩‍💻 Contents

[알고리즘 탑] Dijkstra의 탑 6번 - 무서운 시어머니

image
image

#include <algorithm>
#include <iostream>
#include <queue>

#define MAX_SIZE 1000

using namespace std;

int map[MAX_SIZE][MAX_SIZE];
bool visited[MAX_SIZE][MAX_SIZE];
pair<int, int> directions[4] = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };

int dijkstra(pair<int, int> start = { 0, 0 }, int map_size = MAX_SIZE) {
    priority_queue<pair<int, pair<int, int>>, vector<pair<int, pair<int, int>>>, greater<>> pq;
    pq.emplace(map[start.second][start.first], start);
    visited[start.second][start.first] = true;
    int max_dist = 0;

    while (!pq.empty()) {
        auto [cost, pos] = pq.top();
        auto [cur_x, cur_y] = pos;
        pq.pop();

        for (auto [dx, dy] : directions) {
            int next_x = cur_x + dx;
            int next_y = cur_y + dy;

            if (next_x < 0 || next_x >= map_size || next_y < 0 || next_y >= map_size || map[next_y][next_x] == -1) continue;

            if (!visited[next_y][next_x]) {
                int next_cost = cost + map[next_y][next_x];
                max_dist = max(max_dist, next_cost);
                visited[next_y][next_x] = true;
                pq.emplace(next_cost, make_pair(next_x, next_y));
            }
        }
    }

    return max_dist;
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int y, x, n;
    cin >> y >> x >> n;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            cin >> map[i][j];
        }
    }

    cout << dijkstra({ x, y }, n) << '\n';

    return 0;
}

📱 Screenshot

image

📝 Review Note

일반적인 그래프 상황에서 다익스트라 알고리즘을 적용하는 상황과는 다르게, 이 문제에서는 간선이 아닌 노드가 스스로 가중치를 가지고 있다.

때문에 득정 노드로 들어가는 모든 간선은 같은 가중치를 가지고 있으므로, 특정 노드에 대한 최단 경로는 해당 노드 직전 노드들의 누적 가중치 중 가장 작은 값을 가진 노드가 된다.

특정 노드에 인접한 노드들은 priority queue에 의해 정렬되므로, 특정 노드에 가장 처음 도달한 노드를 통하는 경로가 최단 경로임을 보장할 수 있다.

이 부분은 특정 노드에 도달하기 직전 노드들에도 똑같이 적용이 가능하며, 이를 이용해 귀납적으로 모든 노드에 대하여 가장 먼저 방문하는 경로가 최단 경로임을 보장할 수 있다.

일반적으로 간선에 가중치가 있는 상황을 가정해보자.

A-1-B
|   |
2   100
|   |
C-3-D

위와 같은 상황에서 A노드에서 B노드로의 최소거리를 다익스트라 알고리즘으로 구하게 된다면, 첫 번째 단계에서의 pqB, C 의 우선순위를 가지게 된다.
이로 인해 D 노드에 가장 먼저 방문하는 경로는 A-B-C101거리의 경로가 되고 이는 A-C-D5거리의 경로보다 긴 거리다.

이와 같은 상황때문에 다익스트라 알고리즘BFS와는 다르게 가장 먼저 방문한 경로의 거리가 최소 거리라고 보장하지 못하고 목표 노드로 도착할 수 있는 모든 경로를 탐색한 뒤에 더이상 목표 노드로 가는 더 작은 경로가 없을때에서야 최소 경로를 보장할 수 있디.

그러나 해당 문제와 같이 노드 자체에 가중치가 있다면 각 노드로 들어가는 모든 간선은 같은 가중치를 가지게 된다.

A--1--B
|     |
2     100
|     |
C-100-D

위와 같은 조건 하에는 D노드로 들어가는 최소 경로는 B노드의 최소경로와 C노드의 최소 경로의 우선순위와 동일함을 보장할 수 있게 되고, 이를 귀납적으로 축소해 보면 각 노드에 도착하는 첫번째 경로가 해당 노드로의 최소 경로임이 보장되게 되어서 첫번째 경로 이후로 추가적인 탐색이 필요없다는 결론에 도달하게 된다.

혹시 해당 부분에서 제 논리에 문제점이 있다면 지적 부탁드립니다.

📬 Reference

@minjae9610 minjae9610 requested a review from a team as a code owner August 8, 2023 11:52
@minjae9610 minjae9610 requested review from sjleo1 and calendar2 and removed request for a team August 8, 2023 11:52
@minjae9610 minjae9610 changed the title [cpp] mincoding dijkstra tower 6 [cpp] Dijkstra의 탑 6번 - 무서운 시어머니 Aug 9, 2023
Copy link
Contributor

@Yg-Hong Yg-Hong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코드 감동이네요🧐
cpp 놓은게 고작 올해 초인데 벌써 cpp 코드 읽기가 어려워집니다ㅠ
근래 원래 코드도 pq로 풀었던거 아니었나요??

@minjae9610
Copy link
Member Author

코드 감동이네요🧐

cpp 놓은게 고작 올해 초인데 벌써 cpp 코드 읽기가 어려워집니다ㅠ

근래 원래 코드도 pq로 풀었던거 아니었나요??

@Yg-Hong 원래 코드는 다익스트라 알고리즘을 진행하며 모든 노드에 대한 최소 거리를 전부 구한 상태에서 다시 한번 모든 노드를 순회하며 모든 노드중 최대값을 찾아 돌려주는 방식이었습니다.
바꾼 코드는 목표 노드를 방문하는 첫번째 경로를 발견하면 다른 경로의 탐색을 하지 않고 바로 해당 경로의 거리를 반환하는 방식이고요.

@minjae9610
Copy link
Member Author

minjae9610 commented Aug 10, 2023

@Yg-Hong 일반적으로 간선에 가중치가 있는 상황을 가정해보겠습니다.

A-1-B
|   |
2   100
|   |
C-3-D

위와 같은 상황에서 A노드에서 B노드로의 최소거리를 다익스트라 알고리즘으로 구하게 된다면, 첫 번째 단계에서의 pqB, C 의 우선순위를 가지게 됩니다.
이로 인해 D 노드에 가장 먼저 방문하는 경로는 A-B-C101거리의 경로가 되고 이는 A-C-D5거리의 경로보다 긴 거리입니다.

이와 같은 상황때문에 다익스트라 알고리즘BFS와는 다르게 가장 먼저 방문한 경로의 거리가 최소 거리라고 보장하지 못하고 목표 노드로 도착할 수 있는 모든 경로를 탐색한 뒤에 더이상 목표 노드로 가는 더 작은 경로가 없을때에서야 최소 경로를 보장할 수 있습니다.

그러나 해당 문제와 같이 노드 자체에 가중치가 있다면 각 노드로 들어가는 모든 간선은 같은 가중치를 가지게 됩니다.

A--1--B
|     |
2     100
|     |
C-100-D

위와 같은 조건 하에는 D노드로 들어가는 최소 경로는 B노드의 최소경로와 C노드의 최소 경로의 우선순위와 동일함을 보장할 수 있게 되고, 이를 귀납적으로 축소해 보면 각 노드에 도착하는 첫번째 경로가 해당 노드로의 최소 경로임이 보장되게 되어서 첫번째 경로 이후로 추가적인 탐색이 필요없다는 결론에 도달하게 됩니다.

혹시 해당 부분에서 제 논리에 문제점이 있다면 지적 부탁드립니다.

@Yg-Hong
Copy link
Contributor

Yg-Hong commented Aug 10, 2023

@minjae9610 설명을 너무 잘써주셔서 일단 1따봉 드립니다...ㄷㄷ👍

풀이 이해됐습니당. 설명 잘하시네요.. 좋은 코드 잘봤습니다..!!!

@minjae9610 minjae9610 requested review from a team and removed request for calendar2 and a team August 15, 2023 10:59
@sjleo1
Copy link

sjleo1 commented Aug 16, 2023

저와 알고리즘이 완전 같네요.
항상 민재님 코드 볼 때마다 새로운 C++ 문법을 배우게 되는 것 같습니다.
많이 배워 갑니다

@minjae9610 minjae9610 merged commit 05854ca into main Aug 16, 2023
@minjae9610 minjae9610 deleted the algo/minjae9610/mincoding_dijkstra_tower_6 branch August 16, 2023 03:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants