C# 코딩테스트 강좌, 최단 경로 구하기

이 강좌에서는 C#을 사용하여 알고리즘 시험에서 흔히 출제되는 “최단 경로 구하기” 문제를 풀이해 보겠습니다. 이 주제는 그래프 이론의 기본 개념을 이해하고, 알고리즘 문제 해결 능력을 배양하는 데 매우 유용합니다. 특히, Dijkstra 알고리즘과 같은 인기 있는 알고리즘을 적용하여 이 문제를 어떻게 해결할 수 있는지 살펴보겠습니다.

문제 설명

다음은 “최단 경로 구하기” 문제의 예제입니다:

한 마을에는 N개의 집이 있고, 이 집들은 간선으로 연결되어 있습니다. 각 간선은 이동하는데 소요되는 시간을 나타냅니다. 이제 A 집에서 B 집까지 가는 가장 빠른 경로를 구하세요. 입력으로는 집의 수 N, 간선의 수 M, 각 간선의 정보(시작 집, 끝 집, 소요 시간)가 주어지고, 마지막으로 A 집과 B 집의 번호가 주어집니다.

입력 형식

  • 첫 번째 줄: 집의 수 N (1 ≤ N ≤ 1000)
  • 두 번째 줄: 간선의 수 M (1 ≤ M ≤ 10000)
  • 세 번째 줄부터 M + 2번째 줄까지: 간선 정보 (시작 집, 끝 집, 소요 시간)
  • 마지막 줄: A 집 번호와 B 집 번호

출력 형식

가장 빠른 경로의 소요 시간을 출력합니다. 만약 A 집에서 B 집으로 갈 수 없는 경우 -1을 출력합니다.

해결 방안

이 문제를 해결하기 위해 우리는 그래프 이론의 Dijkstra 알고리즘을 사용합니다. 이 알고리즘은 음의 가중치가 없는 그래프에서 최단 경로를 구하는 데 매우 효과적입니다.

Dijkstra 알고리즘

Dijkstra 알고리즘은 시작 노드에서 다른 모든 노드까지의 최단 경로를 구하는 그리디 알고리즘입니다. 이 알고리즘의 주요 아이디어는 다음과 같습니다:

  1. 출발 노드의 최단 거리를 0으로 초기화하고, 다른 모든 노드의 거리는 무한대로 초기화합니다.
  2. 우선순위 큐를 사용하여 현재 노드에서 가장 거리가 짧은 노드를 선택합니다.
  3. 선택된 노드를 기반으로 인접 노드의 거리 정보를 업데이트합니다.
  4. 모든 노드의 최단 거리를 계산할 때까지 이 과정을 반복합니다.

코드 구현

이제 Dijkstra 알고리즘을 C#으로 구현해보겠습니다. 아래는 알고리즘을 사용하여 최단 경로를 구하는 코드입니다:

using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        int N = int.Parse(Console.ReadLine());
        int M = int.Parse(Console.ReadLine());

        // 그래프 초기화
        List>[] graph = new List>[N + 1];
        for (int i = 1; i <= N; i++)
        {
            graph[i] = new List>();
        }

        // 간선 입력 받기
        for (int i = 0; i < M; i++)
        {
            var line = Console.ReadLine().Split();
            int u = int.Parse(line[0]);
            int v = int.Parse(line[1]);
            int w = int.Parse(line[2]);
            graph[u].Add(new Tuple(v, w));
            graph[v].Add(new Tuple(u, w)); // 양방향 간선
        }

        var lastLine = Console.ReadLine().Split();
        int start = int.Parse(lastLine[0]);
        int end = int.Parse(lastLine[1]);

        // 최단 경로 구하기
        var result = Dijkstra(graph, N, start, end);
        Console.WriteLine(result);
    }

    static int Dijkstra(List>[] graph, int N, int start, int end)
    {
        int[] distances = new int[N + 1];
        bool[] visited = new bool[N + 1];
        int INF = int.MaxValue;

        // 거리를 INF로 초기화
        for (int i = 1; i <= N; i++)
        {
            distances[i] = INF;
        }
        distances[start] = 0;

        // 우선순위 큐
        SortedSet> pq = new SortedSet>();
        pq.Add(new Tuple(0, start));

        while (pq.Count > 0)
        {
            var current = pq.Min;
            pq.Remove(current);

            int currDist = current.Item1;
            int currNode = current.Item2;

            if (visited[currNode])
            {
                continue;
            }
            visited[currNode] = true;

            foreach (var neighbor in graph[currNode])
            {
                int nextNode = neighbor.Item1;
                int weight = neighbor.Item2;

                if (currDist + weight < distances[nextNode])
                {
                    distances[nextNode] = currDist + weight;
                    pq.Add(new Tuple(distances[nextNode], nextNode));
                }
            }
        }

        return distances[end] == INF ? -1 : distances[end];
    }
}

코드 설명

위의 코드는 최단 경로를 구하는 Dijkstra 알고리즘의 구현입니다:

  • 그래프 초기화: 집의 수 N과 간선의 수 M을 입력받고, 그래프를 리스트로 표현합니다.
  • 간선 입력: 각 간선 정보를 입력받아 그래프에 추가합니다. 이때, 양방향 간선으로 처리합니다.
  • Dijkstra 함수: 최단 경로를 구하는 로직을 포함합니다. 거리 배열과 방문 배열을 초기화하고, 우선순위 큐를 사용하여 가장 짧은 거리의 노드를 선택합니다.

결론

이번 강좌에서는 C#을 사용하여 최단 경로 문제를 풀어보았습니다. Dijkstra 알고리즘을 통해 그래프에서 최단 경로를 효과적으로 구할 수 있다는 것을 배웠습니다. 이러한 기법은 코딩 테스트 및 실제 개발에서도 매우 유용하게 사용될 수 있습니다. 다양한 문제를 풀어보며 이 알고리즘의 기초를 확실히 다져보시길 바랍니다.

이 글이 유익하시다면 댓글과 좋아요를 부탁드립니다. 다음에는 더 흥미로운 알고리즘 문제로 찾아뵙겠습니다!

C# 코딩테스트 강좌, 여행 계획 짜기

작성자: 조광형

작성일: 2024년 11월 26일

1. 문제 개요

채용 과정에서 많이 다뤄지는 알고리즘 문제 중 하나는 ‘여행 계획 짜기’입니다. 이 문제는 주어진 여러 조건을 충족하면서 여행지를 선정하는 것에 대한 것입니다.
이를 통해 우리는 자료구조와 알고리즘을 활용하여 효율적으로 문제를 해결할 수 있는 능력을 기르게 됩니다. 이번 강좌에서는 C# 언어로 이 알고리즘 문제를 해결하는 과정을 상세히 다뤄보겠습니다.

2. 문제 설명

문제:
여러분은 여행을 계획하고 있습니다. 여행지를 결정하기 위해 여러 도시와 그 도시 간의 거리 정보를 가지고 있습니다.
가장 적은 이동 거리로 모든 도시를 방문하고 다시 출발 도시로 돌아오는 여행 경로를 찾는 프로그램을 작성하세요.
도시들은 그래프 형태로 주어지며, 각 도시 간의 거리는 배열로 표현됩니다.

입력:
– 도시 수 N (1 ≤ N ≤ 10)
– 도시 간의 거리를 나타내는 N x N 배열
– 시작 도시 (0 ≤ 시작 도시 < N)

출력:
– 최소 거리로 모든 도시를 방문한 후 시작 도시로 돌아오는 경로의 거리

3. 문제 분석

이 문제는 전형적인 외판원 문제(TSP: Traveling Salesman Problem)로, NP-hard 문제입니다.
이는 모든 도시를 방문한 후 시작 도시로 돌아오는 최단 경로를 찾는 문제로, 브루트 포스 알고리즘을 통해 모든 경우를 탐색할 수 있습니다.
소규모 도시 수(N ≤ 10)에서는 모든 조합을 검사하는 것이 가능하므로, 조합을 사용하여 문제를 해결할 수 있습니다.
이를 위해 재귀 호출과 비트 마스크 기법을 이용한 동적 프로그래밍을 사용할 것입니다.

4. 알고리즘 설계

우리의 목표는 주어진 도시와 거리 정보를 기반으로 최단 경로를 찾는 것입니다.
이를 위해 다음과 같은 단계로 알고리즘을 설계할 수 있습니다:

1. 입력 데이터를 처리하여 거리 배열과 도시 수를 정의합니다.
2. 재귀적 언어를 사용하여 가능한 모든 경로를 탐색합니다.
3. 각 경로의 거리를 계산하여 최단 거리 정보를 업데이트합니다.
4. 모든 도시를 방문한 후 시작 도시로 돌아오는 경로의 거리와 최소 거리를 비교하여 결과를 도출합니다.

구현할 C# 코드:

5. C# 코드 구현

                
                using System;

                class TravelPlan
                {
                    static int N;
                    static int[,] distance;
                    static int minDistance = int.MaxValue;

                    static void Main(string[] args)
                    {
                        N = Convert.ToInt32(Console.ReadLine());
                        distance = new int[N, N];

                        for (int i = 0; i < N; i++)
                        {
                            var inputs = Console.ReadLine().Split();
                            for (int j = 0; j < N; j++)
                            {
                                distance[i, j] = Convert.ToInt32(inputs[j]);
                            }
                        }

                        int startCity = 0;
                        bool[] visited = new bool[N];
                        visited[startCity] = true;

                        FindShortestPath(startCity, visited, 0, 0, 1);
                        Console.WriteLine(minDistance);
                    }

                    static void FindShortestPath(int currentCity, bool[] visited, int currentDistance, int count)
                    {
                        if (count == N)
                        {
                            currentDistance += distance[currentCity, 0];
                            minDistance = Math.Min(minDistance, currentDistance);
                            return;
                        }

                        for (int nextCity = 0; nextCity < N; nextCity++)
                        {
                            if (!visited[nextCity] && distance[currentCity, nextCity] > 0)
                            {
                                visited[nextCity] = true;
                                FindShortestPath(nextCity, visited, currentDistance + distance[currentCity, nextCity], count + 1);
                                visited[nextCity] = false;
                            }
                        }
                    }
                }
                
            

위의 코드는 입력으로 도시 수와 각 도시 간의 거리 정보를 받아들이고,
시작 도시에서 출발하여 재귀적으로 모든 도시에 방문하는 경로를 탐색하여 최소 거리를 출력하는 코드입니다.
각 도시를 방문할 때마다 방문 여부를 기록하고, 모든 도시를 방문했는지 체크하여 결과를 업데이트합니다.

6. 코드 분석

코드를 살펴보면, FindShortestPath 메소드는 현재 도시에서 방문하지 않은 모든 도시로 이동하며 경로를 탐색합니다.
이 과정에서 visited 배열을 사용하여 방문한 도시를 추적합니다. 모든 도시에 방문한 경우,
시작 도시로 돌아가는 경로의 거리를 계산하고 최소 거리 정보를 업데이트합니다.
이 알고리즘은 재귀적 호출과 백트래킹을 통해 모든 가능한 경로를 점검합니다.

7. 테스트 케이스

이 알고리즘을 테스트하기 위해 몇 가지 테스트 케이스를 작성할 수 있습니다.
예를 들어, 아래와 같은 입력으로 테스트할 수 있습니다:

입력:

                4
                0 10 15 20
                10 0 35 25
                15 35 0 30
                20 25 30 0
                

출력:

                최소 거리: 80
                

이 입력 데이터는 각 도시 간의 거리를 나타내며, 예상 출력은 80입니다.
이는 가장 짧은 경로인 0 → 1 → 3 → 2 → 0의 거리입니다.

8. 최적화 방안

위의 알고리즘은 간단한 구현이지만, N이 커질 경우 시간 복잡도가 많이 증가하여 비효율적입니다.
따라서 메모이제이션을 통해 부분 문제의 결과를 저장할 수 있는 방법을 고려하여 성능을 개선할 수 있습니다.
예를 들어, 비트 마스크와 동적 프로그래밍을 사용하여 각 상태를 미리 계산하고 결과를 저장하면,
연속된 재귀 호출을 줄여 실행 시간을 크게 감소시킬 수 있습니다.

9. 결론

이번 강좌에서는 C#을 사용해 여행 계획 짜기 문제를 해결하는 방법을 다뤘습니다.
이 과정을 통해 알고리즘 문제 해결 능력을 키울 수 있으며,
여기에 추가적으로 메모이제이션을 활용한 최적화 기법도 학습할 수 있습니다.
다양한 알고리즘 문제를 풀어보며 자신의 논리적 사고와 코딩 능력을 한층 더 발전시킬 수 있기를 바랍니다.

C# 코딩테스트 강좌, 다리 놓기

안녕하세요, 여러분! 오늘은 C#을 사용하여 코딩 테스트에서 자주 등장하는 문제, 다리 놓기에 대해 알아보겠습니다. 이 문제는 조합(combination)과 동적 프로그래밍(dynamic programming)의 개념을 잘 이해해야 해결할 수 있습니다. 우리가 풀어볼 다리 놓기 문제를 보다 잘 이해하기 위해 문제의 정의와 기본적인 접근 방식에 대해 설명하겠습니다.

문제 정의

다리 놓기 문제는 주어진 n개의 다리 중에서 k개의 다리를 선택해 놓는 방법의 수를 구하는 문제입니다. 여기서 k는 n보다 작거나 같아야 합니다. 이 문제는 조합(combination)의 기본 원리를 따르며, 공식적으로 표현하면 아래와 같습니다:

        C(n, k) = n! / (k! * (n - k)!)
    

여기서 n!은 1부터 n까지의 곱을 의미하며, 조합 공식인 C(n, k)는 n개 중 k개를 선택하는 방법의 수를 나타냅니다.

문제 예시

예를 들어, 5개의 다리 중에서 2개의 다리를 선택하는 경우, 다음과 같은 조합이 있을 수 있습니다:

  • 다리 1, 다리 2
  • 다리 1, 다리 3
  • 다리 1, 다리 4
  • 다리 1, 다리 5
  • 다리 2, 다리 3
  • 다리 2, 다리 4
  • 다리 2, 다리 5
  • 다리 3, 다리 4
  • 다리 3, 다리 5
  • 다리 4, 다리 5

즉, 총 10개의 방법이 있습니다. 이러한 개념을 바탕으로, 우리는 문제를 해결할 수 있습니다.

알고리즘 접근법

다리 놓기 문제를 해결하기 위해서는 두 가지 기본 개념이 필요합니다.

1. 조합 공식

이 프로그래밍 문제는 조합 공식을 사용하여 해결할 수 있습니다. C(n, k) 공식에 기반하여 문제를 해결하겠습니다.

C# 코드 구현


        using System;

        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("n과 k의 값을 입력하세요 (예: n k):");
                string[] inputs = Console.ReadLine().Split(' ');
                int n = int.Parse(inputs[0]);
                int k = int.Parse(inputs[1]);

                long result = CalculateCombination(n, k);
                Console.WriteLine($"C({n}, {k}) = {result}");
            }

            static long CalculateCombination(int n, int k)
            {
                if (k > n) return 0;
                if (k == 0 || k == n) return 1;

                long result = 1;
                for (int i = 0; i < k; i++)
                {
                    result = result * (n - i) / (i + 1);
                }
                return result;
            }
        }
        

코드 설명

위의 C# 코드는 사용자가 입력한 n과 k의 값을 바탕으로 조합의 수를 계산합니다. CalculateCombination 메소드는 주어진 n과 k를 이용해 C(n, k)를 계산하는 로직을 실행합니다.

2. 동적 프로그래밍 방법

조합 수를 계산하는 또 다른 방법은 동적 프로그래밍을 사용하는 것입니다. 이를 통해 메모이제이션(memoization) 기법을 사용할 수 있습니다.

C# 코드 구현: 동적 프로그래밍


        using System;

        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("n과 k의 값을 입력하세요 (예: n k):");
                string[] inputs = Console.ReadLine().Split(' ');
                int n = int.Parse(inputs[0]);
                int k = int.Parse(inputs[1]);

                long[,] dp = new long[n + 1, k + 1];

                for (int i = 0; i <= n; i++)
                {
                    for (int j = 0; j <= Math.Min(i, k); j++)
                    {
                        if (j == 0 || j == i)
                            dp[i, j] = 1;
                        else
                            dp[i, j] = dp[i - 1, j - 1] + dp[i - 1, j];
                    }
                }

                Console.WriteLine($"C({n}, {k}) = {dp[n, k]}");
            }
        }
        

코드 설명

위의 동적 프로그래밍 코드에서는 2차원 배열 dp를 사용하여 C(n, k)를 저장합니다. 중첩 루프를 사용하여 각 경우의 수를 계산합니다. 이 코드는 메모이제이션 방식을 통해 O(n * k)의 시간 복잡도로 문제를 해결합니다.

입출력 예시

사용자가 n과 k을 입력하면, 프로그램이 조합의 개수를 출력하는 과정을 예를 들어 보겠습니다:

입력

    5 2
    

출력

    C(5, 2) = 10
    

결론

다리 놓기 문제는 조합의 개념을 이해하고, 이를 프로그래밍적으로 구현하는 방법을 배우는 좋은 연습문제입니다. C#을 사용하여 조합을 계산하는 두 가지 방법을 제공하였으며, 각 방법의 장단점을 이해하는 것이 중요합니다. 여러분도 이 문제를 통해 조합, 동적 프로그래밍에 대한 이해를 높이고, 더 나아가 다양한 알고리즘 문제를 해결할 수 있는 능력을 기르기를 바랍니다.

참고 자료

C# 코딩테스트 강좌, 연속된 자연수의 합 구하기

1. 문제 설명

여러분의 과제로 “연속된 자연수의 합 구하기”라는 문제를 해결해야 합니다. 이 문제는 주어진 수 N에 대해 연속된 자연수들을 더하여 N이 되는 경우의 수를 구하는 것입니다. 즉, 두 개 이상의 자연수로 연속된 합을 통해 N을 생성할 수 있는 경우의 수를 찾는 것입니다.

2. 문제 예시

예제 입력 1

N = 9

예제 출력 1

3

설명: 9는 (4+5), (2+3+4), (1+2+3+4)로 표현 가능하다.

예제 입력 2

N = 15

예제 출력 2

4

설명: 15는 (7+8), (4+5+6), (1+2+3+4+5), (1+2+3+4+5+6)로 표현 가능하다.

3. 접근 방법

문제를 해결하기 위해 다음과 같은 접근 방법을 사용할 수 있습니다. 먼저, 연속된 자연수의 합이 될 수 있는 범위를 찾아야 합니다. 연속된 자연수의 시작값을 start라 하고, 이 값이 N을 초과할 경우 합이 항상 N보다 작아지기 때문에 범위를 넘어가게 됩니다.
또한 연속된 자연수 k개가 있을 때, 이들의 합은 다음과 같이 표현할 수 있습니다:

sum = start + (start + 1) + (start + 2) + ... + (start + (k - 1))

위의 식을 정리하면:

sum = k * start + (0 + 1 + 2 + ... + (k - 1)) = k * start + (k * (k - 1)) / 2

따라서, 이 식을 통해 start 값을 구할 수 있을 것입니다. 이를 통해 자연수 N에 대해 가능한 모든 연속된 경우의 수를 파악할 수 있습니다.

4. 알고리즘 구현

다음은 C# 언어로 구현한 코드입니다:


using System;

class Program
{
    static void Main()
    {
        int N = 9; // 예제 입력
        int count = 0;

        for (int k = 2; k <= N; k++) // k는 연속된 자연수의 개수
        {
            // start는 (N - k * (k - 1) / 2) / k
            int start_sum = N - (k * (k - 1)) / 2;
            if (start_sum > 0 && start_sum % k == 0)
            {
                count++;
            }
        }

        Console.WriteLine(count); // 예제 출력
    }
}
    

위 코드는 주어진 수 N에 대해 두 개 이상의 연속된 자연수의 합을 구하는 방법을 구현하였습니다. k는 연속된 자연수를 나타내며, 시작점을 확인하기 위해 계산합니다. 시작점이 양수이면서 k로 나누어떨어질 경우 카운트를 증가시킵니다.

5. 시간 복잡도 분석

위 알고리즘의 시간 복잡도는 O(N)입니다. 이는 k의 값이 2부터 N까지 반복되는 것을 고려할 때, 선형적으로 증가하여 전체적으로 N에 비례하는 복잡도를 갖습니다. 이 특정 문제는 입력 값 N의 크기가 커지더라도 상대적으로 빠르게 계산이 가능합니다.

6. 결론

연속된 자연수의 합 구하기 문제는 코딩 테스트에서 자주 출제되는 문제로, 기본적인 수학적 접근법과 함께 프로그래밍적인 사고를 요구합니다. 문제 해결 능력을 키우기 위해 다양한 N 값을 입력해보고, 출력되는 결과를 확인하여 이해도를 높일 수 있습니다.
이렇게 연속된 자연수의 합을 구하는 문제를 통해 C#의 기본적인 문법 및 로직 구성 능력을 향상시키고, 실전 코딩 테스트에서의 문제 해결 능력을 배양할 수 있습니다.

7. 추가 연습 문제

본 문제를 해결한 후 더 많은 연습을 원하신다면, 다음과 같은 문제를 시도해보세요.

  • N이 주어졌을 때, N보다 작은 모든 자연수의 합을 구하라.
  • 임의의 두 자연수 A, B가 주어졌을 때, A부터 B까지의 자연수의 합을 구하라.
  • 연속된 자연수의 개수가 주어졌을 때, 이들 연속된 수의 합 S가 주어졌을 때, 가능한 A (시작수)의 모든 조합을 구하라.

C# 코딩테스트 강좌, 최소 비용 구하기

코딩 테스트에서 자주 등장하는 문제 중 하나는 ‘최소 비용 구하기’ 문제입니다.
이 문제는 특정 조건을 만족시키면서 비용을 최소화해야 하는 상황에서,
다양한 알고리즘을 활용하여 효율적으로 해결할 수 있습니다.
이번 글에서는 C#을 이용해 최소 비용 구하기 문제를 해결하는 방법을 자세히 살펴보겠습니다.

문제 설명

주어진 그래프에서 각 간선은 특정 비용을 가지고 있습니다.
시작 정점에서 다른 모든 정점으로 가는 최소 비용 경로를 찾는 문제가 주어졌을 때,
우리는 이를 효율적으로 해결해야 합니다.

문제 정의

정점의 개수는 N, 간선의 개수는 M이라고 가정할 때,
다음과 같은 조건을 만족하는 그래프가 주어진다고 합시다.

  • N (1 <= N <= 1000): 정점의 개수
  • M (1 <= M <= 10000): 간선의 개수
  • 비용은 1 이상 10000 이하의 정수

입력: 시작 정점과 각 정점 간의 비용 정보가 주어진다.
출력: 시작 정점에서 다른 모든 정점으로 가는 최소 비용 경로의 배열을 출력한다.

예시

입력

        5 7
        1 2 2
        1 3 3
        2 3 1
        2 4 5
        3 4 1
        3 5 5
        4 5 2
        1
        

출력

        0 2 3 7 5
        

위의 예시에서 정점 1에서 출발했을 때,
정점 2까지는 2, 정점 3까지는 3, 정점 4까지는 7, 정점 5까지는 5의 비용이 필요합니다.
각 비용이 최소가 되도록 경로를 설정해야 합니다.

알고리즘 설명

문제를 해결하기 위해 Dijkstra 알고리즘을 활용할 것입니다.
Dijkstra 알고리즘은 가중치가 있는 그래프에서 최단 경로를 찾는 알고리즘으로,
다음과 같은 절차로 진행됩니다:

  1. 시작 정점을 설정하고, 이 정점의 거리 값을 0으로 설정합니다.
  2. 모든 다른 정점의 거리 값을 무한대로 초기화합니다.
  3. 가장 비용이 적게 드는 정점을 선택하여 주변 정점으로의 거리 값을 갱신합니다.
  4. 모든 정점에 대해 위 과정을 반복합니다. 최종적으로 각 정점에 대한 최소 비용이 계산됩니다.

C# 구현

이제 Dijkstra 알고리즘을 C#으로 구현해 보겠습니다.
먼저 필요한 라이브러리를 참조하고, 그래프를 표현하기 위해 인접 리스트를 사용하겠습니다.

        using System;
        using System.Collections.Generic;

        class Program
        {
            static void Main(string[] args)
            {
                int n = 5; // 정점 개수
                int m = 7; // 간선 개수
                List> edges = new List>()
                {
                    Tuple.Create(1, 2, 2),
                    Tuple.Create(1, 3, 3),
                    Tuple.Create(2, 3, 1),
                    Tuple.Create(2, 4, 5),
                    Tuple.Create(3, 4, 1),
                    Tuple.Create(3, 5, 5),
                    Tuple.Create(4, 5, 2)
                };
                int start = 1; // 시작 정점

                var result = Dijkstra(n, m, edges, start);
                
                Console.WriteLine(string.Join(" ", result));
            }

            static int[] Dijkstra(int n, int m, List> edges, int start)
            {
                // 그래프 초기화
                List>> graph = new List>>(n + 1);
                for (int i = 0; i <= n; i++)
                {
                    graph.Add(new List>());
                }

                // 간선 추가
                foreach (var edge in edges)
                {
                    graph[edge.Item1].Add(Tuple.Create(edge.Item2, edge.Item3));
                }

                // 거리 초기화
                int[] distance = new int[n + 1];
                bool[] visited = new bool[n + 1];
                for (int i = 1; i <= n; i++) distance[i] = int.MaxValue;

                // 시작 정점
                distance[start] = 0;
                PriorityQueue> pq = new PriorityQueue>();
                pq.Enqueue(Tuple.Create(0, start));

                while (pq.Count > 0)
                {
                    var current = pq.Dequeue();
                    int dist = current.Item1;
                    int node = current.Item2;

                    if (visited[node]) continue;
                    visited[node] = true;

                    foreach (var neighbor in graph[node])
                    {
                        int newDist = dist + neighbor.Item2;
                        if (newDist < distance[neighbor.Item1])
                        {
                            distance[neighbor.Item1] = newDist;
                            pq.Enqueue(Tuple.Create(newDist, neighbor.Item1));
                        }
                    }
                }
                return distance[1..]; // 시작 정점을 제외한 거리 반환
            }
        }

        // 우선순위 큐 클래스 정의
        public class PriorityQueue
        {
            List elements = new List();

            public int Count => elements.Count;

            public void Enqueue(T item)
            {
                elements.Add(item);
                var index = elements.Count - 1;
                while (index > 0)
                {
                    var parentIndex = (index - 1) / 2;
                    if (Compare(elements[index], elements[parentIndex]) >= 0) break;
                    Swap(index, parentIndex);
                    index = parentIndex;
                }
            }

            public T Dequeue()
            {
                var lastIndex = elements.Count - 1;
                var firstElement = elements[0];
                elements[0] = elements[lastIndex];
                elements.RemoveAt(lastIndex);

                --lastIndex;

                int index = 0;
                while (true)
                {
                    var leftChildIndex = index * 2 + 1;
                    var rightChildIndex = index * 2 + 2;
                    int smallestChildIndex;

                    if (leftChildIndex > lastIndex) break;
                    if (rightChildIndex > lastIndex) smallestChildIndex = leftChildIndex;
                    else smallestChildIndex = Compare(elements[leftChildIndex], elements[rightChildIndex]) < 0 ? leftChildIndex : rightChildIndex;

                    if (Compare(elements[index], elements[smallestChildIndex]) <= 0) break;

                    Swap(index, smallestChildIndex);
                    index = smallestChildIndex;
                }

                return firstElement;
            }

            void Swap(int indexA, int indexB)
            {
                var temp = elements[indexA];
                elements[indexA] = elements[indexB];
                elements[indexB] = temp;
            }

            int Compare(T a, T b)
            {
                // 사용자 정의 비교 로직 여기에 구현 필요
                // 예를 들어, Tuple의 경우
                return a.Item1.CompareTo(b.Item1);
            }
        }
        

알고리즘 분석

위의 알고리즘을 사용하면 시간 복잡도는 O((N + M) log N)입니다.
여기서 N은 정점의 수, M은 간선의 수입니다.
Dijkstra 알고리즘은 일반적으로 비효율적이지 않으며,
많은 경우에 최소 비용을 구하는 데 효과적입니다.
단, 모든 간선의 가중치가 음수가 아니어야 한다는 점을 유의해야 합니다.

결론

이번 강좌에서는 C#을 통해 최소 비용 구하기 문제를 해결하기 위한 Dijkstra 알고리즘을
살펴보았습니다. 문제를 정의하고, 알고리즘을 설명한 후
실제 코드를 구현하는 과정을 차근차근 진행했습니다.
코딩 테스트에서 이러한 문제들이 자주 출제되므로, 충분히 연습하고 이해하는 것이 중요합니다.
이번 글이 코딩 테스트 준비에 도움이 되길 바랍니다.