기타/코딩테스트 공부

[필수 알고리즘] 다익스트라

나는야 개발자 2025. 5. 6. 12:37
반응형

작동원리

- 초기값 무대한으로 설정 및 힙 시작점 추가

- 힙에서 현재 노드를 빼며 간선을 통할때 거리가 짧아진다면 거리 갱신 및 힙 추가

 

핵심코드

// [비용, 노드번호] 형태로 저장하기 위한 우선순위 큐 선언
PriorityQueue<int[], int> heap = new PriorityQueue<int[], int>();

// 시작점 초기화
dist[K] = 0;
heap.Enqueue(new int[] {0, K}, 0); // [비용, 노드번호]를 저장하고, 비용을 기준으로 정렬

while (heap.Count > 0)
{
    // 현재 가장 비용이 적은 노드 정보 추출
    int[] current = heap.Dequeue();
    int ew = current[0]; // 비용
    int ev = current[1]; // 노드 번호
    
    // 이미 처리된 노드라면 스킵 (더 짧은 경로를 이미 찾은 경우)
    if (dist[ev] != ew) continue;
    
    // 인접 노드 탐색
    foreach (var next in edge[ev])
    {
        int nw = next.Item1; // 간선 가중치
        int nv = next.Item2; // 인접 노드
        
        // 더 짧은 경로를 발견한 경우 갱신
        if (dist[nv] > ew + nw)
        {
            dist[nv] = ew + nw;
            heap.Enqueue(new int[] {dist[nv], nv}, dist[nv]); // [갱신된 비용, 노드] 추가
        }
    }
}

 

아이디어

- 한점에서 다른 모든 점으로 가는 최단경로 > 다익스트라 사용

- 모든 점 거리 초기값 무한대 설정

- 시작점 거리 0 및 힙 추가

- 힙에서 하나씩 빼며 수행 -> 현재 거리가 새로운 간선 이동 시 크다면 갱신 -> 새로운 거리 힙 추

 

시간복잡도

- ElgV : E = 3e5, lgV = 20

- O(ElgV) = 6e6

 

변수

- 힙(비용(int), 다음노드(int))[]

- 거리 배열 : int[]

- 간선, 인접리스트 :(비용(int), 다음노드(int))[]

 

TIP

- 코드는 그냥 외우기

- 한점에 다른 점으로 가는 최소 비용이라고 했을때 사용하기

 

문제풀이

- 백준 1753

 

1. 

class num_1753
{
    static void Main(string[] args)
    {
        var sr = new StreamReader(Console.OpenStandardInput());
        var sw = new StreamWriter(Console.OpenStandardOutput());

        string[] input = sr.ReadLine().Split();
        //정점 개수
        int V = int.Parse(input[0]);
        //간선 개수
        int E = int.Parse(input[1]);
        //시작점
        int K = Convert.ToInt32(sr.ReadLine());

        //노드별 비용과 간선수 리스트
        var edge = new List<List<Tuple<int, int>>>();
        for (int i = 0; i <= V; i++)
        {
            edge.Add(new List<Tuple<int, int>>());
        }
    }
}

- 정점, 간선, 시작점 input 받기

- 노드별로 비용, 간선 수를 저장할 List<List<Tuple<int,int>>>(); 초기화

단, 1부터 시작할 것이기 때문에 i <= V 로 처리

 

2.

  //최소값을 구하기 위한 값 배열, MaxValue로 초기화
int[] dist = new int[V + 1];
for (int i = 0; i <= V; i++)
{
    dist[i] = int.MaxValue;
}


for (int i = 0; i < E; i++)
{
    string[] input_ = sr.ReadLine().Split();
    //노드번호
    int u = int.Parse(input_[0]);
    //간선개수
    int v = int.Parse(input_[1]);
    //비용
    int w = int.Parse(input_[2]);

    edge[u].Add(new Tuple<int, int>(w, v));
}

- 노드별 비용을 저장하기 위한 int[] dist 추가 후 int 최대값 저장

- u,v,w 값을 Input받은 후 edge[u]로 노드별로 w,v(비용, 간선수) 저장

 

3.

PriorityQueue<int[], int> heap = new PriorityQueue<int[], int>();

//시작점 초기화
dist[K] = 0;
//시작점이기 때문에 비용은 0 시작노드 K 로 초기화
heap.Enqueue(new int[] { 0, K }, 0);


while (heap.Count > 0)
{
    var qdata = heap.Dequeue();
    int w = qdata[0];
    int node = qdata[1];

    //현재 node의 비용값이 다르면 힙 새로 가져오기
    if (dist[node] != w)
    {
        continue;
    }

    //
    foreach (var next in edge[node])
    {
        //다음 노드의 비용
        int nw = next.Item1;
        //다음 노드의 연결된 노드번호
        int nv = next.Item2;

        //다음노드의 비용이 가져온 노드의 비용과 다음 노드의 비용 비교 
        if (dist[nv] <= w + nw)
        {
            continue;
        }
        dist[nv] = w + nw;
        //더 작다면 현재 노드의 비용과 노드 번호 힙에 추가
        heap.Enqueue(new int[] { dist[nv], nv }, dist[nv]);
    }
}

- 우선순위 큐를 생성하여 시작점인 k를 이용해 dist[k]를 0으로 초기화(k에서 k로 이동하는데 비용이 들지 않기 때문에 0값)

- heap을 꺼내서 if(dist[node] != w)로 현재 꺼낸 노드 정보가 최신값인지 체크하여 불필요한 연산이나 중복처리 방지

- edge[node]의 정보를 꺼내 dist[nv] <= w +nw로 값이 큰지 체크 후 작다면 정보 갱신 및 노드 정보 heap에 추가

 

4.

//출력
for (int i = 1; i <= V; i++)
{
    if (dist[i] == int.MaxValue)
    {
        sw.WriteLine("INF");
    }
    else
    {
        sw.WriteLine(dist[i]);
    }
}

sw.Flush();
sw.Close();
sr.Close();

- 최종 dist 정보 출력하여 int.MaxValue값이 있으면 INF로 출력

 

최종코드

/*
1. 아이디어
- 한점 시작, 모든거리: 다익스트라
- 간선, 인접리스트 저장
- 거리배열 무한대 초기화
- 시작점 : 거리배열 0, 힙에 넣어주기
- 힙에서 빼며 다음 것을 수행
    -> 최신값인지 먼저 확인
    -> 간선을 타고 간 비용이 더 작으면 갱신

2. 시간복잡도
- 다익스트라 : O(ElgV)
    -> E : 3e5
    -> V : 2e4, lgV ~= 20
    -> ElgV = 6e6

3. 변수
- 힙 : (비용, 노드번호)
- 거리 배열(비용) : int[]
- 간선 저장, 인접리스트 : (비용, 노드번호)[]

*/

class num_1753
{
    static void Main(string[] args)
    {
        var sr = new StreamReader(Console.OpenStandardInput());
        var sw = new StreamWriter(Console.OpenStandardOutput());

        string[] input = sr.ReadLine().Split();
        //정점 개수
        int V = int.Parse(input[0]);
        //간선 개수
        int E = int.Parse(input[1]);
        //시작점
        int K = Convert.ToInt32(sr.ReadLine());

        //노드별 비용과 간선수 리스트
        var edge = new List<List<Tuple<int, int>>>();
        for (int i = 0; i <= V; i++)
        {
            edge.Add(new List<Tuple<int, int>>());
        }

        //최소값을 구하기 위한 값 배열, MaxValue로 초기화
        int[] dist = new int[V + 1];
        for (int i = 0; i <= V; i++)
        {
            dist[i] = int.MaxValue;
        }


        for (int i = 0; i < E; i++)
        {
            string[] input_ = sr.ReadLine().Split();
            //노드번호
            int u = int.Parse(input_[0]);
            //간선개수
            int v = int.Parse(input_[1]);
            //비용
            int w = int.Parse(input_[2]);

            edge[u].Add(new Tuple<int, int>(w, v));
        }

        PriorityQueue<int[], int> heap = new PriorityQueue<int[], int>();

        //시작점 초기화
        dist[K] = 0;
        //시작점이기 때문에 비용은 0 시작노드 K 로 초기화
        heap.Enqueue(new int[] { 0, K }, 0);


        while (heap.Count > 0)
        {
            var qdata = heap.Dequeue();
            int w = qdata[0];
            int node = qdata[1];

            //현재 node의 비용값이 다르면 힙 새로 가져오기
            if (dist[node] != w)
            {
                continue;
            }

            //
            foreach (var next in edge[node])
            {
                //다음 노드의 비용
                int nw = next.Item1;
                //다음 노드의 연결된 노드번호
                int nv = next.Item2;

                //다음노드의 비용이 가져온 노드의 비용과 다음 노드의 비용 비교 
                if (dist[nv] <= w + nw)
                {
                    continue;
                }
                dist[nv] = w + nw;
                //더 작다면 현재 노드의 비용과 노드 번호 힙에 추가
                heap.Enqueue(new int[] { dist[nv], nv }, dist[nv]);
            }
        }

        //출력
        for (int i = 1; i <= V; i++)
        {
            if (dist[i] == int.MaxValue)
            {
                sw.WriteLine("INF");
            }
            else
            {
                sw.WriteLine(dist[i]);
            }
        }

        sw.Flush();
        sw.Close();
        sr.Close();
    }
}

 

참고영상

- 바로가기

반응형

'기타 > 코딩테스트 공부' 카테고리의 다른 글

[필수 알고리즘] 폴로이드  (0) 2025.05.06
[필수 알고리즘] MST  (0) 2025.05.05
[필수 알고리즘] DP  (0) 2025.05.05
[필수 알고리즘] 그리디  (0) 2025.05.05
[필수 알고리즘] 이진탐색  (0) 2025.05.03