C# 코딩테스트 강좌, 동적 계획법 알아보기

코딩테스트에서 알고리즘 문제를 효과적으로 해결하는 방법 중 하나는 동적 계획법(Dynamic Programming, DP)입니다. 동적 계획법은 복잡한 문제를 더 간단한 하위 문제로 나누어 해결하는 접근 방식으로, 최적화 문제를 해결하고 계산을 줄이는 데 매우 유용합니다. 이번 강좌에서는 동적 계획법의 기초부터 시작하여 실제 문제를 통해 이론을 살펴보고, C# 프로그래밍 언어를 사용하여 문제를 해결하는 방법을 단계별로 설명하겠습니다.

동적 계획법의 이해

동적 계획법은 기본적으로 두 가지 중요한 성질인 최적 부분 구조중복 쌍 문제를 활용합니다. 최적 부분 구조는 문제의 최적 해결 방법이 그 하위 문제의 최적 해결 방법으로 구성된다는 것을 의미합니다. 중복 쌍 문제는 동일한 하위 문제를 여러 번 해결하는 대신, 그 결과를 저장하여 필요할 때 재사용하는 것입니다. 이를 통해 효율성을 획기적으로 높일 수 있습니다.

동적 계획법의 적용 예시

동적 계획법은 다음과 같은 문제에 사용됩니다:

  • 피보나치 수열 계산
  • 최장 공통 부분 수열(longest common subsequence)
  • 최소 경로 문제
  • 0-1 배낭 문제
  • 동적 행렬 곱셈

문제 소개: 0-1 배낭 문제

이번 강좌에서는 0-1 배낭 문제를 통해 동적 계획법을 적용해 보겠습니다. 문제는 다음과 같이 설명됩니다:

무게의 제한이 있는 배낭이 있습니다. 각 아이템은 고유한 무게와 가치를 가집니다. 각 아이템은 0개 또는 1개만 사용할 수 있으며, 배낭에 담을 수 있는 최대 가치는 무엇인지 계산하세요.

예시로 다음과 같은 아이템 목록이 있다고 가정해 봅시다:

아이템 무게 가치
아이템 1 1 1
아이템 2 2 6
아이템 3 3 10
아이템 4 5 16

배낭의 최대 무게는 7입니다. 이 경우 최대 가치는 얼마일까요?

문제 풀이 과정

1단계: 문제 정의

우리는 재귀적인 사고 방식으로 문제를 해결하려고 할 수 있지만, 이는 중복된 계산을 야기할 수 있습니다. 따라서, 동적 계획법을 사용하여 하위 문제의 해결책을 저장하고 재사용하는 방식으로 접근합니다.

2단계: 상태 정의

먼저 우리가 해결해야 할 문제를 정의합니다. dp[i][w]를 사용하여 첫 i개의 아이템을 고려했을 때, 무게 w를 초과하지 않으면서 최대 가치를 저장합니다. i는 아이템의 인덱스, w는 배낭의 무게입니다.

3단계: 상태 전이 방정식

이제 상태 전이 방정식을 정의해야 합니다. i 번째 아이템을 포함하는 경우와 포함하지 않는 경우를 나누어 생각합니다.

  • 아이템 i를 포함하지 않을 경우: dp[i][w] = dp[i-1][w]
  • 아이템 i를 포함할 경우(이때 아이템의 무게가 w를 넘어서는 안 됨): dp[i][w] = dp[i-1][w – weight[i]] + value[i]

최종적으로, 두 경우 중 최대값을 선택합니다:

dp[i][w] = max(dp[i-1][w], dp[i-1][w - weight[i]] + value[i])

4단계: 초기 조건 정의

기본적으로 아이템이 없거나 무게가 0인 경우에는 최대 가치는 0입니다. 따라서 다음과 같이 초기화합니다:

for w in range(max_weight + 1):
    dp[0][w] = 0
for i in range(n + 1):
    dp[i][0] = 0

5단계: 최종 솔루션

모든 하위 문제를 해결한 후, dp 배열의 마지막 요소는 최적의 해를 나타냅니다. 이를 출력하여 결과를 확인합니다.

6단계: C# 코드 구현


using System;

class Program
{
    static void Main(string[] args)
    {
        int[] weights = new int[] { 1, 2, 3, 5 };
        int[] values = new int[] { 1, 6, 10, 16 };
        int maxWeight = 7;
        int n = weights.Length;

        int[,] dp = new int[n + 1, maxWeight + 1];

        // 초기화
        for (int w = 0; w <= maxWeight; w++)
            dp[0, w] = 0;
        
        for (int i = 0; i <= n; i++)
            dp[i, 0] = 0;

        // 동적 계획법 적용
        for (int i = 1; i <= n; i++)
        {
            for (int w = 1; w <= maxWeight; w++)
            {
                if (weights[i - 1] <= w)
                    dp[i, w] = Math.Max(dp[i - 1, w], dp[i - 1, w - weights[i - 1]] + values[i - 1]);
                else
                    dp[i, w] = dp[i - 1, w];
            }
        }

        Console.WriteLine("최대 가치: " + dp[n, maxWeight]);
    }
}

결과 확인

위 코드를 실행하면 주어진 배낭 문제에 대한 최대 가치를 출력하게 됩니다. 이 경우 결과는 17이 됩니다. 이는 아이템 2와 아이템 3을 선택하여 최대 무게를 초과하지 않으면서 최대 가치를 얻을 수 있음을 의미합니다.

결론

이번 강좌를 통해 동적 계획법의 기초와 0-1 배낭 문제를 해결하는 과정을 살펴보았습니다. 동적 계획법은 많은 알고리즘 문제에서 활용될 수 있으며, 문제 해결 능력을 키우는 데 큰 도움을 줄 것입니다. 연습을 통해 다양한 문제를 해결해 보시고, 더 깊이 있는 이해를 쌓으시길 바랍니다.

다음 강좌에서는 더 다양한 동적 계획법 응용문제를 다루어 보겠습니다. 감사합니다!