WonYong-Jang / algorithm

0 stars 0 forks source link

Dijkstra / Bellman-ford / (SPFA ) / Floyd-Warshall 알고리즘 #3

Open WonYong-Jang opened 6 years ago

WonYong-Jang commented 6 years ago

다익스트라 알고리즘은

그래프의 어떤 정점 하나를 시작점으로 선택하고, 나머지 정점들로의 최단거리를 모두 구함 시작점 자신은 0 정점 개수가 V, 간선 개수가 E일 때 기본적인 최적화를 거치면 O(ElogV)의 시간복잡도

// 인접 리스트 정점 갯수만큼 할당 해주고 인접리스트 연결 할 것 // dis 배열 모두 INF 로 초기화 해주고 시작점 0으로 놓고 시작 ! public static void solve() { PriorityQueue que = new PriorityQueue<>(new Mysort()); que.add(new Node(S,0)); // 시작점 dis[S] = 0;

while(!que.isEmpty())
{
    Node n = que.poll();

    if(n.cost > dis[n.dx]) continue;

    for(Node next : adj.get(n.dx)) // 연결된 다음 노드들을 보면서 최단경로 탐색 
    {
        // 현재 노드에서 다음 노드로 가는 가중치를 더한 값이 dis[next.dx] 보다 작으면 그 경로가 최단 경로가 되니 업데이트 
        if(dis[next.dx] > next.cost + dis[n.dx])  
        {
            dis[next.dx] = next.cost + dis[n.dx];
            que.add(new Node(next.dx, dis[next.dx]));
        }
    }
}

}

**만약 음수사이클 있다면 이 과정이 성립하지 않아서, 최단 경로를 구하지 못함** 
이때는 조금 더 느리지만 전용 알고리즘인 벨만 포드 알고리즘을 써야 함

백준 관련문제(최단경로) : https://www.acmicpc.net/problem/1753
우선순위 큐 다익스트라 : http://manducku.tistory.com/32

## 벨만포드 알고리즘
- V개의 정점과 E개의 간선을 가진 가중 그래프에서 특정 출발정점에서부터 모든 정점까지의 최단 경로를 구하는 알고리즘
- **음의 가중치 가지는 간선이 있어도 가능**
- **음의 사이클의 존재 여부도 확인 가능** 
- 최단 거리를 구하기 위해서 V-1 번 E개의 모든 간선을 확인하고 , 음의 사이클 존재여부 확인하기 위해 E개의 간선을 확인하므로 **시간복잡도는 O(VE)**
<img width="537" alt="2018-08-07 12 01 01" src="https://user-images.githubusercontent.com/26623547/43751861-a1d82c96-9a39-11e8-8688-0618cf6cf92b.png">

/**



참고 링크 : http://blog.naver.com/PostView.nhn?blogId=kks227&logNo=220796963742

## 플로이드 워셜 알고리즘
- V개의 정점과 E개의 간선을 가진 가중 그래프 에서 모든 정점 사이의 최단 경로를 구하는 알고리즘
- 순환만 없다면 음수 가중치도 가능!
- 동적계획법으로 접근 !
- **시간복잡도 O(N^3) 이기 때문에 제한시간 1초 이내에 들어오려면 N=500 이내만 사용할 것!**
<img width="572" alt="2018-08-07 12 04 03" src="https://user-images.githubusercontent.com/26623547/43752007-32c17d7a-9a3a-11e8-88c6-8ab4b0e6f728.png">

- 2차원 배열 ans 에는 두 정점 간의 비용이 초기화 되어 있어야함
- 두 정점 간의 연결이 없다면 무한대로 초기화
- 무한대로 초기화 하는 이유는 이 무한대를 이용하여 두 정점간의 연결 여부를 파악하기 위해
- 대부분 같은 정점끼리는 주어지지 않기때문에 i == j 인 경우는 0으로 초기화

관련 문제 : 2660번 회장뽑기, 1956번 운동, 1613번 역사, 10159번 저울, 1238번 파티, 1389번 케빈 베이컨의 6단계 법칙
WonYong-Jang commented 5 years ago

SPFA ( Shortest Path Faster Algorithm)

타임머신 소스코드

public class Main {

    static int N, M;
    static int INF = 987654321;
    static ArrayList<ArrayList<Node>> adj = new ArrayList<>(); 
    static int[] dis = new int[505];
    static int[] inQ = new int[505];
    static int[] cycle = new int[505];
    static Queue<Integer> que = new LinkedList<>();
    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        for(int i=0; i<= N; i++) {
            adj.add(new ArrayList<>());
            dis[i] = INF;
        }
        int dx =0, dy =0, cost = 0;
        for(int i=1; i<= M; i++) {
            st = new StringTokenizer(br.readLine());
            dx = Integer.parseInt(st.nextToken());
            dy = Integer.parseInt(st.nextToken());
            cost = Integer.parseInt(st.nextToken());
            adj.get(dx).add(new Node(dy, cost));
        }

        que.add(1);
        dis[1] = 0; //최단 거리 배열 
        inQ[1] = 1; // 큐에 들어있는지 확인 배열 
        cycle[1]++;
        while(!que.isEmpty())
        {

            int cur = que.poll();
            inQ[cur] = 0;

            for(Node next : adj.get(cur))
            {
                if(dis[next.dx] > next.cost + dis[cur])
                {
                    dis[next.dx] = next.cost + dis[cur];
                    if(inQ[next.dx] == 0)
                    {   
                        cycle[next.dx]++;
                        if(cycle[next.dx] >= N) {
                            System.out.println(-1);
                            return;
                        }
                        inQ[next.dx] = 1;
                        que.add(next.dx);
                    }
                }
            }
        }

        for(int i=2; i<= N; i++)
        {
            if(dis[i] == INF) System.out.println(-1);
            else System.out.println(dis[i]);
        }
    }
    static class Node {
        int dx, cost;
        Node(int a, int b) { 
            dx=a; cost=b;
        }
    }
}
WonYong-Jang commented 5 years ago

다익스트라 응용

Deque 를 이용한 다익스트라

image

문제 : 전구를 켜라

https://koitp.org/problem/BOI_2011_BULB/read/

스크린샷 2019-06-14 오후 10 14 10
WonYong-Jang commented 5 years ago

K번째 최단 경로 찾기 (백준 1854 )

- dist를 우선순위 큐 ( 내림차순) 배열로 만들어두고 next의 최단 거리 갯수가 k 보다 작다면 계속 추가해주고 k 와 같다면 가장 큰것들과 비교해서 더작은 값으로 갱신해주게 된다면 dist[i] 배열의 peek() 값은 k번째 경로가 들어 있게 된다.

따라서 dist[i].size() 가 k개가 들어있다면 해당경로로 가는 k 번째 최단경로가 존재!! 그렇지 않다면 k번째로 가는 최단경로가 존재하지 않는다!!

while(!que.isEmpty())
{
    Node n = que.poll();

    if(n.cost > dist[n.dx].peek()) continue; // 시간 컷트 ! 

    for(Node next : adj[n.dx])
    {
        long nextCost = next.cost + n.cost;
        if(dist[next.dx].size() < K) // k 개 가 될때까지 삽입 
        {
            dist[next.dx].add(nextCost);
            que.add(new Node(next.dx, nextCost));
        }
        else // 크기가 k 개가 되었다면 가장 큰  top 과 비교하여 갱신 
        {
            if(dist[next.dx].peek() > nextCost)
            {
                dist[next.dx].poll();
                dist[next.dx].add(nextCost);
                que.add(new Node(next.dx, nextCost));
            }
        }
    }
WonYong-Jang commented 4 years ago

leetcode 787. Cheapest Flights Within K Stops ( 벨만포드 문제 )

class Solution {

    public final int INF = 1<<30;
    public int[][] dist;
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {

        dist = new int[K+2][n];
        for(int[] d : dist)
        {
            Arrays.fill(d, INF);   
        }

        dist[0][src] = 0;
        for(int i=0; i<= K; i++)
        {
            dist[i][src] = 0;
            for(int[] cur : flights)
            {
                if(dist[i][cur[0]] != INF && dist[i+1][cur[1]] > cur[2] + dist[i][cur[0]])
                {
                    dist[i+1][cur[1]] = cur[2] + dist[i][cur[0]];
                }
            }
        }

        return dist[K+1][dst] == INF ? -1 : dist[K+1][dst] ;

    }
    public int min(int a, int b) { return a > b ? b: a;}

}