1956번(플로이드 워셜)

2023. 2. 28. 11:29·CS 이론/알고리즘
728x90

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

다익스트라 알고리즘으로 특정 정점에서 다른 특정 정점까지의 최단거리를 확인하였다. 그렇다면 모든 정점에서 모든 정점으로 가는 최단거리는 어떻게 구할까? 이때 사용하는 알고리즘이 플로이드 워셜이다. 플로이드 워셜은 모든 정점에 대해서 모든 정점으로 가는 최단거리를 중간 지점 노드를 사용하여 계산한다.

음의 간선 사이클이 존재하지 않는다면 음의 가중치를 가진 경우에도 최단 거리를 구할 수 있다는 장점이 있다.

 

동작 방식은 간단하다.

주어진 간선에 대해서 2차원 배열로 정의하고, 모든 노드에 대해서 모든 노드로 이동하는 과정에서 지나는 중간지점을 설정하여 최단거리를 계산하는 것이다.

예를 들면, 아래의 2차원 배열을 봐보자. 1에서 2로 가는 가중치가 1이고, 1에서 3으로 가는 가중치가 5이다.

2에서 3으로 가는 가중치는 2이고, 2에서 1로는 간선이 없다. 3에서 1도 간선이 없다. 3에서 2로는 가중치가 1이다.

  1 2 3
1 0 1 5
2 INF 0 2
3 INF 1 0

이 배열에서 중간에 거쳐가는 지점을 2라고 해보자.

그렇다면, 1에서 2로가는데 1의 가중치이고 2에서 3으로 가는데 2의 가중치임으로 1에서 3으로 가는 가중치는 5에서3(5->3)이된다.

1->2->3으로 이동하면서 1->3으로 이동하는 것보다 가중치가 작으니 갱신하는 것이다.

이를 반복적으로 모든 정점에 대해서 수행한다.

그러면 결과적으로 아래와 같은 결과가 나온다.

  1 2 3
1 0 1 3
2 INF 0 2
3 INF 1 0

모든 정점에 대해서 최단거리를 구하면 위의 표처럼 된다.

하지만 이 문제를 풀기 위해서는 한 단계 더 나아가야한다. 우리가 구하려는 것은 사이클이다. 즉, 자기자신으로 돌아오는 최단거리를 찾아야한다. 따라서 자기자신으로 돌아오는 가중치를 0으로 했던 것에서 무한(INF)으로 만들어서 최단거리를 구해야한다.

따라서 최단거리 표의 초기화를 아래와 같이한다. 이렇게 하면 0이 아닌 최단거리 사이클을 저장할 수 있다.

  1 2 3
1 INF 1 5
2 INF INF 2
3 INF 1 INF

그래서 플로이드 워셜 로직을 봐보면, 아래와 같다.

public void fld_ws(){
    for(int k=1; k<=V; k++){
        for(int i=1; i<=V; i++){
            for(int j=1; j<=V; j++){
                if(dp[i][k]!=Integer.MAX_VALUE&&dp[k][j]!=Integer.MAX_VALUE){
                    dp[i][j]=Math.min(dp[i][j],dp[i][k]+dp[k][j]);
                }
            }
        }
    }
    /*for(int i=0; i<=V; i++){
        for(int j=0; j<=V; j++){
            if(dp[i][j]==Integer.MAX_VALUE){
                System.out.print("INF ");
            }else System.out.print(dp[i][j]+" ");
        }
        System.out.println();
    }*/
    int min=Integer.MAX_VALUE;
    for(int i=1; i<=V; i++){
        if(min>dp[i][i]){
            min=dp[i][i];
        }
    }
    if(min==Integer.MAX_VALUE){
        System.out.println(-1);
    }
    else System.out.println(min);
}

최단거리 저장 2차원배열을 갱신하고 그중에서 자기자신으로 돌아오는 값중에 최소를 구하면된다.

 

그래서 정답 코드는 아래와 같다.

package backjoon.b1956;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class b1956 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int V = Integer.parseInt(st.nextToken());//마을 수
        int E = Integer.parseInt(st.nextToken());//다리 수
        int[][] dp = new int[V+1][V+1];
        for(int i=0; i<E; i++){
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());//시작노드
            int b = Integer.parseInt(st.nextToken());//도착노드
            int c = Integer.parseInt(st.nextToken());//가중치
            dp[a][b]=c;
        }
        /*  1 2 3
        * 1 0 1 5
        * 2 INF 0 2
        * 3 INF 1 0
        * */

        for(int i=1; i<=V; i++){
            for(int j=1; j<=V; j++){
                if(dp[i][j]==0){
                    dp[i][j]=Integer.MAX_VALUE;
                }
            }
        }

        Graph g = new Graph(V,dp);
        g.fld_ws();
    }
}
class Graph{
    int[][] dp;
    int V;
    public Graph(int V, int[][] dp) {
        this.dp=dp;
        this.V=V;
    }
    public void fld_ws(){
        for(int k=1; k<=V; k++){
            for(int i=1; i<=V; i++){
                for(int j=1; j<=V; j++){
                    if(dp[i][k]!=Integer.MAX_VALUE&&dp[k][j]!=Integer.MAX_VALUE){
                        dp[i][j]=Math.min(dp[i][j],dp[i][k]+dp[k][j]);
                    }
                }
            }
        }
        /*for(int i=0; i<=V; i++){
            for(int j=0; j<=V; j++){
                if(dp[i][j]==Integer.MAX_VALUE){
                    System.out.print("INF ");
                }else System.out.print(dp[i][j]+" ");
            }
            System.out.println();
        }*/
        int min=Integer.MAX_VALUE;
        for(int i=1; i<=V; i++){
            if(min>dp[i][i]){
                min=dp[i][i];
            }
        }
        if(min==Integer.MAX_VALUE){
            System.out.println(-1);
        }
        else System.out.println(min);
    }
}
728x90

'CS 이론 > 알고리즘' 카테고리의 다른 글

11505번  (0) 2023.03.02
11404번(플로이드 워셜)  (0) 2023.03.01
2042번  (0) 2023.02.26
2293번  (0) 2023.02.25
24313번  (0) 2023.02.22
'CS 이론/알고리즘' 카테고리의 다른 글
  • 11505번
  • 11404번(플로이드 워셜)
  • 2042번
  • 2293번
Bello's
Bello's
개발하는 벨로
  • Bello's
    벨로의 개발일지
    Bello's
  • 전체
    오늘
    어제
    • 분류 전체보기 (199) N
      • 노예 일지 (7)
        • 스타트업 노예일지 (3)
      • CS 이론 (81)
        • 학과 수업 (4)
        • 알고리즘 (64)
        • 시스템 프로그래밍 (3)
        • 데이터 통신 (1)
        • 운영체제 (2)
        • 데이터베이스 (1)
      • project (3)
      • 나는 감자다. (4)
      • Spring (27)
      • 모각코 (45)
        • 절개와지조(모각코) (7)
        • 어쩌다보니 박준태가 조장이조 (11)
        • 어쩌다보니 박준태가 또 조장이조 (12)
      • LikeLion🦁 (20)
      • 캘리포니아 감자 (4)
      • OpenSource Contribute (1)
      • 우아한테크벨로 (1) N
        • 프리코스 회고록 (6) N
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    JPA
    누적합
    타임리프
    백준
    회고록
    감자
    DFS
    뛰슈
    Spring
    절개와지조
    그래프 순회
    오블완
    어렵다
    프리코스
    BFS
    티스토리챌린지
    자바
    모각코
    나는 감자
    8기
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
Bello's
1956번(플로이드 워셜)
상단으로

티스토리툴바