끄르뀨 2022. 8. 17. 22:07

문제

https://www.acmicpc.net/problem/1956

 

1956번: 운동

첫째 줄에 V와 E가 빈칸을 사이에 두고 주어진다. (2 ≤ V ≤ 400, 0 ≤ E ≤ V(V-1)) 다음 E개의 줄에는 각각 세 개의 정수 a, b, c가 주어진다. a번 마을에서 b번 마을로 가는 거리가 c인 도로가 있다는 의

www.acmicpc.net

 

풀이 과정

플로이드-워셜(floyd-warshall) 알고리즘을 이용한다. 해당 알고리즘은 2차원 배열을 이용하여 두 노드를 잇는 최단거리를 구하는 알고리즘이다. 이때 노드의 최단 거리를 구할 때 자기 자신의 노드는 0으로 설정하는데, 이 부분만 수정한다면 위의 문제를 해결할 수 있다.

 

아래의 그림은 플로이드 워셜 알고리즘이 적용되기 전, 후의 상태를 나타낸다.

출처 : Foundations of Algorithms 5th edition

 

플로이드 워셜 알고리즘의 핵심은 두 노드를 잇는 최단거리를 계속 기록하면서 업데이트하는 것에 있다.

i와 j를 잇는 거리에 대해 k를 경유하여 가는 것이 더 짧다면 이전에 기록되었던 길이 대신 더 짧은 길을 기록하는 것을 의미한다.

이전에 기록되었던 최단 거리들이 어떤 경로로 선택되었던 것인지는 상관없고 더 짧기만 하다면 짧은 거리를 배열에 기록하는 방식이다.

 

 

백준 1956번 운동 문제는 시작 지점으로 다시 돌아오는 cycle의 거리를 알고 싶다.

따라서 자기 자신까지의 최단 거리를 0으로 설정하지 않고 무한대로 설정한 뒤 플로이드-워셜 알고리즘을 적용한다면 자기 자신에게서 출발해서 다시 자기 자신으로 돌아오는 거리를 배열에 기록할 수 있다. 이후 dist [i][i]에 기록된 사이클의 최단 길이를 기록하며 탐색한다.

 

이때 최단 길이가 무한대로 설정한 값 그대로라면, 주어진 조건에는 사이클이 존재지 않는 것을 의미하며 -1을 출력한다. 그렇지 않으면 기록된 최단 거리를 출력한다.

 

 

소스 코드

#include <iostream>
#include <algorithm>
#include <cstring>
#define INF 9999999

using namespace std;
const int max_v = 401;
int dist[max_v][max_v];

int v,e;

void print_ary(int ary[][max_v]){
    for(int i=1;i<=v;i++){
        for(int j=1;j<=v;j++){
            cout << ary[i][j] << " ";
        }
        cout << "\n";
    }
}


int main(){
    //Input
    cin >> v >> e;
    for(int i=1;i<=v;i++){
        for(int j=1;j<=v;j++){
            dist[i][j]=INF;
        }
    }

    //Init dist
    for(int i=0; i<e; i++){
        int st, ed, d;
        cin >> st >> ed >> d;
        dist[st][ed] = d;
    }

    //floyd algorithm
    for(int k=1; k<=v; k++){
        for(int i=1;i<=v;i++){
            for(int j=1;j<=v;j++){
                dist[i][j] = min(dist[i][j],dist[i][k]+dist[k][j]);
            }
        }
    }

    //find shortest cycle
    int ans = INF;
    for(int i=1;i<=v;i++){
        ans = min(ans, dist[i][i]);
    }
    if(ans == INF) ans = -1;
    cout << ans;

    return 0;
}

 

결과 및 후기

초기 제출 시 INF 값 설정을 생각보다 작게 잡아서 채점 시 64%에서 오답이 발생했었다. 해당 INF 값을 더 크게 설정하니 모든 케이스를 통과할 수 있었다.