알고리즘 모음(C++)

백준 10217 - KCM Travel(C++) 본문

백준

백준 10217 - KCM Travel(C++)

공대생의 잡다한 사전 2022. 12. 30. 18:45

문제 링크입니다. https://www.acmicpc.net/problem/10217

 

10217번: KCM Travel

각고의 노력 끝에 찬민이는 2014 Google Code Jam World Finals에 진출하게 되었다. 구글에서 온 초대장을 받고 기뻐했던 것도 잠시, 찬찬히 읽어보던 찬민이는 중요한 사실을 알아차렸다. 최근의 대세

www.acmicpc.net

Dp와 다익스트라 알고리즘을 합펴서 푸는 문제였습니다.

이동 비용이 M이하이면서, 최소시간을 구하는 문제입니다.

 

1에서 N까지 이동할 때, 최소 거리를 구하는 방법은 간단합니다.

하지만, N까지 이동할 수 있는 비용은 다를 수 있습니다.

따라서 2차원 배열을 이동해, X번 노드를 Y비용에 도착했을 경우로 만들어줘야 합니다.

 

저는 Distance[X][Y] = K라는 배열을 만들어, "X번 노드를 Y 비용으로 도착했을 때, K 비용이 들었다" 의 의미로 만들었습니다.

 

배열을 통해 기억하면서 풀기에 탐색 과정에서 비용의 조건만 추가하면 바로 맞을 수 있습니다.

 

여기서 코드 시간을 줄이는 방법을 생각해보겠습니다.

예를 들어, 7번 노드를 X비용으로 오는데 10초라는 시간이 걸렸다고 가정하겠습니다.

이때, X+1, X+2 비용일때는 10초보다 큰 시간이 걸렸습니다.

그렇다면, X+1. X+2 비용에서 탐색할 때는 각각의 시간으로 탐색하는 것이 좋을꺼 같습니까?

당연히 해당 경우도 10초라는 시간을 가지고 탐색하는 것이 효율적일 것입니다.

 

따라서 Y비용으로 X정점을 도착했을 때의 시간을 Y+1 ~ M까지의 비용과 비교해봅니다. 

비교하면서 자신보다 큰 시간이 있을 경우, 자신의 시간으로 바꿔줍니다. 

자신보다 작은 비용이 나올 때 까지만 반복하면 됩니다.

 

왜나하면, Y+K라는 비용일 때의 시간이 더 작다면 해당 비용 이후부터는 Y+K 비용일 때의 시간으로 바꿔서 이동하는 것이 효율적이기 때문입니다.

 

또한 이번에 탐색하려는 정점과 비용일때의 시간이 Distance에 저장된 값보다 크다면?

-> 전 탐색에서 더 효율적인 방법으로 바뀌었다는 의미입니다. 따라서 해당 경우에는 탐색할 필요가 없어집니다.

 

 

자세한 것은 코드를 참고해주세요.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#define F first
#define S second
#define INF 987654321
#define LL long long int 
#define P pair<int, int>
#define PP pair<int, P>

using namespace std;

int test_case;
int N, M, K;
vector<PP> connect[101]; // node, cost, time
int Distance[101][10001];

void reset_distance(){
    for(int i = 1; i <= N; i++){
        for(int j = 0; j <= M; j++){
            Distance[i][j] = INF;
        }
    }
}

void Dijstra(){
    priority_queue<PP> q; // time, node, cost
    Distance[1][0] = 0;
    q.push({-0, {1,0}});
    while(!q.empty()){
        int x = q.top().S.F;
        int cost = q.top().S.S;
        int T = -q.top().F;
        q.pop();
        if(Distance[x][cost] < T) continue;
        for(int i = 0; i < connect[x].size(); i++){
            int xx = connect[x][i].F;
            int n_cost = connect[x][i].S.F + cost;
            int n_T = connect[x][i].S.S + T;
            if(n_cost <= M && n_T < Distance[xx][n_cost]){
                for(int j = n_cost + 1; j <= M; j++){ // 실행 시간 줄이기
                    if(Distance[xx][j] < n_T) break;
                    Distance[xx][j] = n_T;
                }
                Distance[xx][n_cost] = n_T;
                q.push({-Distance[xx][n_cost],{xx,n_cost}});
            }
        }
    }
}

void reset_connect(){
    for(int i = 1; i <= N; i++) connect[i].clear();
}

void solve(){
    reset_distance();
    Dijstra();
    reset_connect();
    int ans = INF;
    for(int i = 1; i <= M; i++){
        ans = min(ans, Distance[N][i]);
    }
    if(ans == INF) cout << "Poor KCM" << "\n";
    else cout << ans << "\n";
}

int main() {
    cin.tie(0);
    cout.tie(0);
    cin >> test_case;
    for(int i = 0; i < test_case; i++){
        cin >> N >> M >> K;
        for(int j = 1; j <= K; j++){
            int x, y, cost, t;
            cin >> x >> y >> cost >> t;
            connect[x].push_back({y,{cost,t}});
        }
        solve();
    }
    return 0;
}

 

 

질문 및 조언은 댓글을 남겨주세요

'백준' 카테고리의 다른 글

백준 19538 - 루머(C++)  (0) 2023.01.01
백준 2211 - 네트워크 복구(C++)  (2) 2023.01.01
백준 17396 - 백도어(C++)  (0) 2022.12.30
백준 16509 - 장군(C++)  (0) 2022.12.28
백준 16940 - BFS 스페셜 저지(C++)  (0) 2022.12.28