C# 코딩테스트 강좌, 부녀회장이 될 테야

문제 설명

주어진 문제는 다음과 같습니다. 아파트의 층수와 각 층의 거주자 수에 대하여 부녀회장이 되기 위한
알고리즘을 작성해야 합니다. 특정 층의 거주자 수를 계산하는 알고리즘을 구현하는 것이 목표입니다.

아파트는 0층부터 N층(0 ≤ N ≤ 14)까지 있으며, n층의 k호에는 k명이 살고 있습니다.
예를 들어, 3층의 3호에는 3명이 살고 있습니다.
각 층의 주민 수는 아래와 같이 계산됩니다.

– 1층 : 1호 1명, 2호 2명, 3호 3명…
– 2층 : 1호 1명, 2호 3명, 3호 6명…
– n층의 k호에는 n층의 k-1호의 주민 수와 n층의 k호의 주민 수의 합이 저장된다.

입력 형식

첫 번째 줄에는 테스트 케이스의 수 T(1 ≤ T ≤ 100)가 주어집니다. 이후 T개의 줄에 걸쳐 각 테스트케이스마다
정수 N(0 ≤ N ≤ 14)과 K(1 ≤ K ≤ 14)가 제공됩니다. 여기서 N은 층수, K는 호수를 의미합니다.

출력 형식

각 테스트 케이스마다 n층 k호의 거주자 수를 출력해야 합니다.

예제 입력


2
1 3
2 3

예제 출력


3
6

문제 해결 과정

이 문제를 해결하기 위해, 우리는 동적 프로그래밍(Dynamic Programming: DP) 기법을 사용할 것입니다.
n층의 k호의 주민 수를 구하기 위해 아래의 관계를 사용할 수 있습니다.


주민수(n, k) = 주민수(n-1, 1) + 주민수(n, k-1)

여기서, 주민수(0, k) = k 그리고 주민수(n, 0) = 0으로 초기화합니다. 아래는 주민 수를 테이블에 저장하는
과정의 기본 단계입니다.

1단계: 초기화

먼저, 거주자 수를 저장하기 위한 2차원 배열을 선언하고 초기값을 설정합니다.


int[,] dp = new int[15, 15]; // 15층, 15호를 위한 배열
for (int i = 0; i <= 14; i++) {
dp[0, i] = i; // 0층의 k호에는 k명이 살고 있습니다.
}

2단계: DP 테이블 채우기

이중 반복문을 사용하여 dp 테이블을 채워나갑니다. n층과 k호의 경우를 모두 고려하여
아래와 같이 구합니다.


for (int n = 1; n <= 14; n++) {
for (int k = 1; k <= 14; k++) {
dp[n, k] = dp[n - 1, k] + dp[n, k - 1];
}
}

3단계: 결과 출력

모든 테스트 케이스에 대해 주민 수를 구하고 출력합니다. 아래는 최종 구현 예제입니다.


using System;
class Program {
static void Main(string[] args) {
int[,] dp = new int[15, 15];
for (int i = 0; i <= 14; i++) {
dp[0, i] = i;
}
for (int n = 1; n <= 14; n++) {
for (int k = 1; k <= 14; k++) {
dp[n, k] = dp[n - 1, k] + dp[n, k - 1];
}
}
int T = int.Parse(Console.ReadLine());
for (int t = 0; t < T; t++) {
string[] input = Console.ReadLine().Split();
int n = int.Parse(input[0]);
int k = int.Parse(input[1]);
Console.WriteLine(dp[n, k]);
}
}
}

결론

이번 문제를 통해 동적 프로그래밍의 기초 개념을 이해하고, 문제 해결 능력을 향상시킬 수 있었습니다.
이 문제는 알고리즘 테스트에서 자주 출제되는 유형 중 하나로, 다양한 변형이 존재합니다.
실제 코딩테스트에 응용하기 위해서는 DP 기법을 반복적으로 연습하는 것이 중요합니다.

C# 코딩테스트 강좌, 최장 공통 부분 수열 찾기

문제 설명

주어진 두 문자열 A와 B가 있을 때, 이들 문자열의 최장 공통 부분 수열(Longest Common Subsequence, LCS)을 찾는 문제입니다. 부분 수열은 문자열에서 문자의 순서를 유지하면서 삭제로 얻을 수 있는 문자열입니다. 즉, A의 부분 수열이 B에 존재해야 합니다.

예를 들어, 문자열 A가 “ABCBDAB”이고 B가 “BDCAB”라면, 최장 공통 부분 수열은 “BCAB”입니다. 이러한 LCS 문제는 다양한 분야에서 효율적인 알고리즘을 필요로 하는 문제입니다.

문제 입력

두 문자열 A와 B의 길이 N, M (1 ≤ N, M ≤ 1000) 및 문자열 A와 B가 주어집니다.

문제 출력

최장 공통 부분 수열의 길이를 출력합니다.

문제 해결 과정

1. 동적 프로그래밍 이해

이 문제는 동적 프로그래밍(DP)을 사용하여 해결할 수 있습니다. DP는 복잡한 문제를 작은 부분 문제로 나누어 해결하여 효율성을 높이는 기법입니다. 이 경우, DP 배열을 사용하여 각 문자 쌍 간의 LCS 길이를 저장하고 계산합니다.

2. DP 배열 초기화

우선, 두 문자열 A와 B의 길이에 따라 2차원 DP 배열을 초기화합니다. DP[i][j]는 A의 첫 i개 문자와 B의 첫 j개 문자의 최장 공통 부분 수열의 길이를 나타냅니다.


    int[,] dp = new int[N + 1, M + 1];
    

3. DP 상태 전이

DP 규칙은 다음과 같습니다:

  • 만약 A의 i번째 문자와 B의 j번째 문자가 같으면: dp[i][j] = dp[i - 1][j - 1] + 1
  • 만약 A의 i번째 문자와 B의 j번째 문자가 다르면: dp[i][j] = Math.Max(dp[i - 1][j], dp[i][j - 1])

4. 최종 결과

DP 배열이 채워진 후, dp[N][M]에는 최장 공통 부분 수열의 길이가 저장됩니다.

코드 구현


    using System;

    class Program
    {
        static void Main(string[] args)
        {
            string A = "ABCBDAB";
            string B = "BDCAB";
            int N = A.Length;
            int M = B.Length;

            int[,] dp = new int[N + 1, M + 1];

            for (int i = 1; i <= N; i++)
            {
                for (int j = 1; j <= M; j++)
                {
                    if (A[i - 1] == B[j - 1])
                    {
                        dp[i, j] = dp[i - 1, j - 1] + 1;
                    }
                    else
                    {
                        dp[i, j] = Math.Max(dp[i - 1, j], dp[i, j - 1]);
                    }
                }
            }

            Console.WriteLine("최장 공통 부분 수열의 길이: " + dp[N, M]);
        }
    }
    

테스트 케이스

예제 입력


    A: ABCBDAB
    B: BDCAB
    

예제 출력


    최장 공통 부분 수열의 길이: 4
    

시간 복잡도 분석

이 동적 프로그래밍 알고리즘의 시간 복잡도는 O(N * M)이며, 공간 복잡도 또한 O(N * M)입니다. 이를 통해 두 문자열의 길이가 1000일 때도 충분히 성능을 만족하는 알고리즘입니다.

결론

최장 공통 부분 수열(LCS) 문제는 컴퓨터 과학의 여러 분야에서 어떻게 알 수 있는지를 보여주는 주요 문제입니다. 동적 프로그래밍을 사용하여 효율적으로 해결할 수 있음을 확인했습니다. 이 방법은 실제 코딩테스트에서 수시로 등장하므로, 잘 이해하고 연습하는 것이 중요합니다.

C# 코딩테스트 강좌, 특정 거리의 도시 찾기

안녕하세요, 여러분. 오늘은 C#을 활용하여 “특정 거리의 도시 찾기” 문제를 해결하는 방법에 대해 알아보겠습니다. 이 문제는 코딩테스트에서 종종 출제되는 그래프와 BFS(너비 우선 탐색) 알고리즘을 활용한 문제입니다. 그러므로 이 강좌를 통해 그래프 이론과 알고리즘에 대한 이해를 높이는데 큰 도움이 될 것입니다.

문제 설명

주어진 조건을 기반으로 한 문제를 소개합니다.

문제:

N개의 도시가 있고, M개의 양방향 도로가 존재합니다. 각 도시는 1부터 N까지 번호가 매겨져 있으며, 두 도시 A와 B를 연결하는 도로가 있을 때는 A와 B를 연결하는 도로의 길이는 항상 1로 간주합니다. 여러분은 특정 도시 X와 거리 K를 주어질 때, 거리 K인 도시들을 모두 출력해야 합니다.

입력 형식:
첫째 줄에 도시의 개수 N, 도로의 개수 M, 거리 K, 시작 도시 X가 주어진다.
이후 M개의 줄에 걸쳐서 각각의 도로가 연결하는 두 도시가 주어진다.

출력 형식:
거리 K인 도시의 번호를 오름차순으로 출력한다.
만약 그런 도시가 없다면 -1을 출력한다.

입력 예시

4 4 2 1
1 2
1 3
2 3
2 4

출력 예시

4

문제 분석

이 문제를 해결하기 위해서는 그래프를 표현하고 탐색하는 방법이 필요합니다.
각 도시는 정점, 도로는 간선으로 표현할 수 있습니다.
우리는 BFS 또는 DFS 알고리즘을 사용하여 특정 거리를 찾아야 합니다.
이 문제의 경우 거리 K를 찾으므로 BFS가 더 적합합니다. 왜냐하면 BFS는 가까운 노드부터 탐색하기 때문에 거리 개념을 그대로 활용할 수 있기 때문입니다.

문제 해결 과정

1단계: 데이터 구조 정의

먼저, 도시와 도로를 표현하기 위해서 리스트와 큐를 사용할 것입니다.
– 리스트는 각 도시와 연결된 도로를 저장합니다.
– 큐는 BFS를 수행할 때 필요한 데이터 구조입니다.

2단계: 그래프 생성

도시와 도로 정보를 입력으로 받아서 인접 리스트를 생성합니다.
이 때, 데이터를 1 인덱스부터 사용하기 위해 크기를 N + 1로 설정합니다.

3단계: BFS 구현

BFS 함수를 구현하여 출발 도시 X에서 시작합니다.
거리가 K인 도시를 찾아서 결과를 저장합니다.

4단계: 결과 출력

결과 리스트를 정렬하고 출력합니다.
만약 거리가 K인 도시가 없다면 -1을 출력합니다.

C# 코드 구현

다음은 주어진 문제를 해결하기 위한 C# 코드입니다.

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    static List[] graph;
    static bool[] visited;
    static List result;
    
    public static void Main(string[] args)
    {
        // 입력 받기
        string[] input = Console.ReadLine().Split(' ');
        int N = int.Parse(input[0]); // 도시의 수
        int M = int.Parse(input[1]); // 도로의 수
        int K = int.Parse(input[2]); // 거리
        int X = int.Parse(input[3]); // 시작 도시
        
        // 그래프 초기화
        graph = new List[N + 1];
        for (int i = 1; i <= N; i++)
        {
            graph[i] = new List();
        }
        
        // 도로 정보 입력
        for (int i = 0; i < M; i++)
        {
            input = Console.ReadLine().Split(' ');
            int a = int.Parse(input[0]);
            int b = int.Parse(input[1]);
            graph[a].Add(b);
            graph[b].Add(a);
        }
        
        // BFS 및 결과 생성
        visited = new bool[N + 1];
        result = new List();
        BFS(X, 0, K);
        
        // 결과 출력
        result.Sort();
        if (result.Count == 0)
        {
            Console.WriteLine(-1);
        }
        else
        {
            foreach (int city in result)
            {
                Console.WriteLine(city);
            }
        }
    }
    
    static void BFS(int start, int distance, int K)
    {
        Queue> queue = new Queue>();
        queue.Enqueue(new Tuple(start, distance));
        visited[start] = true;
        
        while (queue.Count > 0)
        {
            var current = queue.Dequeue();
            int currentCity = current.Item1;
            int currentDistance = current.Item2;
            
            // 이미 K의 도시를 찾은 경우
            if (currentDistance == K)
            {
                result.Add(currentCity);
                continue;
            }
            
            // 인접 도시 탐색
            foreach (var neighbor in graph[currentCity])
            {
                if (!visited[neighbor])
                {
                    visited[neighbor] = true;
                    queue.Enqueue(new Tuple(neighbor, currentDistance + 1));
                }
            }
        }
    }
}

코드 설명

위의 C# 코드를 통해 문제를 해결하는 과정을 간략히 설명하겠습니다.

  • 데이터 입력: 첫 줄에서 도시 수, 도로 수, 거리 K, 시작 도시 X 값을 읽고, M개의 도로 정보를 입력받습니다.
  • 그래프 구성: 도시를 정점으로, 도로를 간선으로 표현하기 위해 인접 리스트를 구성합니다.
  • BFS 알고리즘 구현: Queue를 사용하여 BFS를 실행하고 각 도시까지의 거리를 계산합니다.
  • 결과 출력: 정렬된 결과를 반환하며, 거리 K와 일치하는 도시가 없을 경우 -1을 출력합니다.

마무리

이번 강좌에서는 “특정 거리의 도시 찾기” 문제를 C#을 활용하여 해결하는 방법을 알아보았습니다.
BFS를 통해 거리 기반 탐색을 하는 과정에서 그래프 데이터 구조와 탐색 알고리즘의 중요성을 느낄 수 있었기를 바랍니다.
다음 강좌에서는 더욱 다양한 문제를 함께 해결해 보도록 하겠습니다.

참고: 문제가 복잡해질수록 BFS와 DFS의 시간을 분별하여 사용하는 것이 중요합니다.
적절한 자료구조의 선택 또한 성능 향상에 도움이 됩니다.

C# 코딩테스트 강좌, 스택으로 오름차순 수열 만들기

문제 설명

주어진 수열에서 스택을 이용하여 오름차순으로 정렬된 수열을 만들 수 있는지 여부를 판단하는 문제입니다. 입력으로 주어진 수열을 스택을 활용하여 가장 작은 수부터 순서대로 출력할 수 있어야 합니다. 만약 불가능한 경우 “NO”, 가능하다면 “YES”를 출력해야 합니다.

문제 예시

  • 입력: 5
  • 입력 수열: 5 1 2 3 4
  • 출력: YES
  • 입력: 7
  • 입력 수열: 1 2 5 3 4 6 7
  • 출력: NO

접근 방법

이 문제는 스택의 특성을 활용하여 해결할 수 있습니다. 스택은 후입선출(LIFO) 구조로, 마지막에 들어온 요소가 가장 먼저 나가는 특성을 가지고 있습니다. 따라서 수열을 오름차순으로 정렬하기 위해서는 입력된 수를 스택에 저장하고, 필요한 경우에만 스택에서 요소를 꺼내어야 합니다.

1단계: 입력 처리

우선 입력으로 주어진 수열을 읽어야 합니다. 수열의 길이와 해당 수열의 원소를 받아옵니다.

2단계: 스택에 요소 저장

입력된 수들을 순차적으로 처리하면서 현재 출력해야 할 수와 비교해야 합니다. 스택을 활용하여 현재 수를 저장하고, 가장 작은 수부터 출력할 수 있도록 합니다.

3단계: 출력 조건 확인

이미 출력해야 할 수보다 큰 수가 스택에 쌓였다면, 해당 수를 더 이상 꺼낼 수 없으므로 “NO”를 출력합니다. 모든 요소를 처리 후 가능한 경우에는 “YES”를 출력합니다.

C# 코드 구현

아래는 위에서 설명한 알고리즘을 C# 언어로 구현한 코드입니다.


using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        int n = int.Parse(Console.ReadLine());
        Stack stack = new Stack();
        int current = 1;

        for (int i = 0; i < n; i++)
        {
            int num = int.Parse(Console.ReadLine());

            while (current <= num)
            {
                stack.Push(current);
                current++;
            }

            if (stack.Count == 0 || stack.Pop() != num)
            {
                Console.WriteLine("NO");
                return;
            }
        }

        Console.WriteLine("YES");
    }
}
    

코드 설명

위의 C# 코드는 다음과 같이 동작합니다:

  • int n = int.Parse(Console.ReadLine());
    사용자로부터 수열의 길이를 입력받습니다.
  • Stack stack = new Stack();
    스택을 초기화합니다.
  • int current = 1;
    현재 출력해야 하는 수를 나타냅니다.
  • 반복문을 통해 입력된 수 각각에 대해:
    • while (current <= num)
      입력된 수보다 현재 수가 작거나 같을 경우, 현재 수를 스택에 추가하고 현재 수를 증가시킵니다.
    • if (stack.Count == 0 || stack.Pop() != num)
      스택에서 가장 위의 수를 꺼내어 입력된 수와 비교합니다. 만약 스택이 비어있거나 다르다면 “NO”를 출력합니다.
  • 모든 수를 처리한 후에 “YES”를 출력합니다.

복잡도 분석

이 알고리즘의 시간 복잡도는 O(n)입니다. 각 수를 한 번씩만 스택에 넣고 빼기 때문입니다. 공간 복잡도 또한 O(n)으로 스택에 최악의 경우 모든 수가 쌓일 수 있습니다.

결론

이 문제는 스택의 특성을 활용하여 해결할 수 있는 전형적인 문제입니다. 코딩 테스트에서는 이러한 스택 문제를 종종 접할 수 있으므로, 스택과 큐의 동작 방식을 충분히 이해하고 있을 필요가 있습니다. 이번 강좌에서는 스택을 사용하여 오름차순으로 수열을 만드는 방법을 배웠습니다. 다양한 문제를 해결하기 위해 이런 기초적인 알고리즘을 연습하는 것이 중요합니다.

추가 연습문제

스택을 이용하여 다른 알고리즘 문제를 풀어보세요:

  • 괄호 균형Checking 문제
  • 후위 표기법 계산기

참고 자료

더 많은 알고리즘을 배우고 싶다면 다음 자료를 참고하세요:

C# 코딩테스트 강좌, 경로 찾기

안녕하세요! 이번 글에서는 코딩테스트에서 자주 등장하는 알고리즘 문제 중 하나인 경로 찾기 문제를 다뤄보겠습니다. 이 강좌에서는 문제의 정의, 해법, C# 코드 구현 등을 자세히 설명하겠습니다. 문제를 해결하기 위해 필요한 알고리즘의 기초부터 시작하여, 복잡한 경로 탐색 알고리즘을 적용하는 방법까지 알아보도록 하겠습니다.

문제 정의

문제는 다음과 같습니다:

주어진 2차원 grid에서 시작점 S에서 도착점 E까지의 최단 경로를 찾으세요. grid는 0과 1로 이루어져 있으며, 0은 이동 가능한 칸, 1은 벽을 의미합니다. 경로는 상하좌우 인접한 칸으로만 이동할 수 있습니다. 경로가 존재하는 경우, 최단 경로의 길이를 반환하고, 존재하지 않는 경우 -1을 반환하세요.

예제

다음은 예제 입력과 출력입니다:

입력:
grid = [
  [0, 0, 1, 0, 0],
  [0, 0, 0, 0, 1],
  [0, 1, 1, 0, 0],
  [0, 0, 0, 0, 0],
  [1, 1, 1, 1, 0]
]
start = (0, 0)  // S의 위치
end = (4, 4)    // E의 위치

출력:
최단 경로의 길이 = 8

문제 해결 접근

문제를 해결하기 위해 다양한 방법이 있지만, BFS (너비 우선 탐색) 알고리즘을 사용할 것입니다. BFS는 최단 경로 문제를 해결하는 데 매우 효과적인 방법입니다. BFS는 현재 노드와 인접한 노드를 차례로 탐색하고, 모든 노드를 탐색한 후 최단 경로를 반환합니다.

알고리즘 설명

BFS 알고리즘의 기본 구조는 다음과 같습니다:

  1. 시작점을 큐에 넣고, 방문 체크를 합니다.
  2. 큐가 비어있지 않은 동안 반복합니다:
    • 큐에서 현재 위치를 꺼냅니다.
    • 현재 위치가 도착점이라면 최단 경로의 길이를 반환합니다.
    • 현재 위치와 인접한 모든 칸을 확인합니다:
      • 이동 가능한 칸이면서 방문하지 않은 칸이라면, 큐에 추가하고 방문 체크를 합니다.
  3. 모든 경로를 탐색한 후에도 도착점에 도달하지 못하면 -1을 반환합니다.

C# 코드 구현

이제 위에서 설명한 알고리즘을 C# 코드로 구현해 보겠습니다.


using System;
using System.Collections.Generic;

class Program {
    public static int ShortestPath(int[,] grid, (int, int) start, (int, int) end) {
        int rows = grid.GetLength(0);
        int cols = grid.GetLength(1);
        
        int[] directions = {0, 1, 0, -1, 0}; // 상하좌우 이동

        Queue<((int, int), int)> queue = new Queue<((int, int), int)>();
        bool[,] visited = new bool[rows, cols];
        
        queue.Enqueue((start, 0)); // (위치, 경로의 길이)
        visited[start.Item1, start.Item2] = true;

        while (queue.Count > 0) {
            var (current, length) = queue.Dequeue();

            if (current == end) {
                return length;
            }

            for (int i = 0; i < 4; i++) {
                int newRow = current.Item1 + directions[i];
                int newCol = current.Item2 + directions[i + 1];

                if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols && 
                    grid[newRow, newCol] == 0 && !visited[newRow, newCol]) {
                    
                    queue.Enqueue(((newRow, newCol), length + 1));
                    visited[newRow, newCol] = true;
                }
            }
        }

        return -1; // 도착점에 도달할 수 없는 경우
    }

    public static void Main() {
        int[,] grid = {
            {0, 0, 1, 0, 0},
            {0, 0, 0, 0, 1},
            {0, 1, 1, 0, 0},
            {0, 0, 0, 0, 0},
            {1, 1, 1, 1, 0}
        };
        
        var start = (0, 0);
        var end = (4, 4);
        int result = ShortestPath(grid, start, end);
        Console.WriteLine("최단 경로의 길이: " + result);
    }
}

코드 리뷰

위의 코드는 BFS 알고리즘을 사용하여 경로를 찾는 방법을 보여줍니다. 코드의 각 부분을 자세히 살펴보겠습니다.

변수 설명

  • rowscols: grid의 행과 열의 수를 저장합니다.
  • directions: 상하좌우로 이동하기 위한 방향을 정의합니다.
  • queue: BFS를 위한 큐입니다. (위치, 경로의 길이) 쌍을 저장합니다.
  • visited: 각 칸의 방문 여부를 체크하는 2D 배열입니다.

큐의 작동

큐에서 현재 위치를 꺼내고, 모든 인접한 칸을 확인하여 이동 가능한 경우 큐에 추가합니다. 이 과정이 반복되면서 최단 경로를 찾습니다.

성능 분석

이 알고리즘의 시간 복잡도는 O(V + E)입니다. 여기서 V는 정점의 수(2D grid의 모든 칸)이고 E는 간선의 수(상하좌우 인접한 칸의 수)에 해당합니다. 공간 복잡도는 O(V)로, BFS를 하기 위한 큐와 방문 체크에 필요한 공간을 차지합니다.

결론

이번 글에서는 C#을 사용하여 경로 찾기 문제를 다뤘습니다. BFS 알고리즘을 활용하여 최단 경로를 찾는 과정을 설명하였고, 이를 코드로 구현해 보았습니다. 이처럼 알고리즘 문제를 풀 때는 문제를 정확히 이해하고, 적절한 알고리즘을 선택하여 접근하는 것이 중요합니다. 여러분도 다양한 문제를 통해 실력을 향상시킬 수 있기를 바랍니다!

감사합니다!