C# 코딩테스트 강좌, 소수 & 팰린드롬 수 중에서 최솟값 찾기

이번 강좌에서는 C#을 활용한 코딩테스트 문제를 다루어보겠습니다.
주제는 “주어진 범위 내의 소수와 팰린드롬 수 중에서 최솟값 찾기”입니다.
이 문제는 알고리즘 사고를 중시하며, 효율적인 코드를 작성하기 위해
다양한 접근 방식을 배울 수 있습니다.

문제 설명

주어진 정수 N에 대해,
1부터 N까지의 정수 중에서 소수이면서
팰린드롬인 수를 모두 찾아 그 중 최솟값을 구하는 문제입니다.
소수란 1과 자기 자신 외에 다른 약수를 가지지 않는 양의 정수를 말하며,
팰린드롬 수란 앞에서 읽으나 뒤에서 읽으나 같은 수를 말합니다.

입력

– 정수 N (1 ≤ N ≤ 1,000,000)

출력

– 소수이면서 팰린드롬 수 중에서 최솟값을 출력하세요.
– 단, 그러한 수가 없을 경우 -1을 출력해야 합니다.

예제 입력

31

예제 출력

11

알고리즘 분석

이 문제를 해결하기 위해서는 다음과 같은 단계로 접근할 수 있습니다:

  1. 소수 판별: 주어진 범위 내의 수가 소수인지 확인하는 알고리즘을 구현합니다.
  2. 팰린드롬 판별: 각 수가 팰린드롬인지 확인하는 함수를 작성합니다.
  3. 최솟값 찾기: 소수이면서 팰린드롬인 수를 모은 후 최솟값을 반환합니다.

C# 코드 구현

아래는 C#을 사용하여 문제를 해결하는 코드입니다.


using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        int N = int.Parse(Console.ReadLine());
        int minPrimePalindrome = int.MaxValue;

        for (int i = 2; i <= N; i++)
        {
            if (IsPrime(i) && IsPalindrome(i))
            {
                minPrimePalindrome = Math.Min(minPrimePalindrome, i);
            }
        }

        Console.WriteLine(minPrimePalindrome == int.MaxValue ? -1 : minPrimePalindrome);
    }

    static bool IsPrime(int number)
    {
        if (number < 2) return false;
        for (int i = 2; i <= Math.Sqrt(number); i++)
        {
            if (number % i == 0) return false;
        }
        return true;
    }

    static bool IsPalindrome(int number)
    {
        string str = number.ToString();
        int len = str.Length;
        for (int i = 0; i < len / 2; i++)
        {
            if (str[i] != str[len - 1 - i]) return false;
        }
        return true;
    }
}
    

코드 설명

1. Main 메서드: 입력받은 정수 N에 대해 2부터 N까지의 수를 반복합니다.
각 수가 소수이면서 팰린드롬인지 확인하고, 그 중 최솟값을 찾습니다.

2. IsPrime 메서드: 주어진 수가 소수인지 판단합니다.
2보다 작은 경우는 소수가 아니므로 false를 반환하고,
2부터 √number까지 나누어 떨어지는 수가 있는지를 검사하여 소수 여부를 결정합니다.

3. IsPalindrome 메서드: 주어진 수를 문자열로 변환 후,
앞과 뒤에서부터 비교하여 팰린드롬 여부를 확인합니다.
모든 문자가 일치하면 true를 반환합니다.

시간 복잡도

이 알고리즘의 시간 복잡도는 O(N√N)입니다.
N까지의 수를 검사할 때, 각 수에 대해 소수 판별에 O(√N)의 시간이 소요되기 때문입니다.
따라서 입력의 범위가 커질수록 연산 시간이 영향을 받으므로,
이점에 유의하여 코드를 작성해야 합니다.

결론

이번 강좌에서 다룬 문제를 통해 소수를 판별하고 팰린드롬을 확인하는 방법을 익혔습니다.
알고리즘 문제를 해결하는 데 있어 소수와 팰린드롬에 대한 이해는
코딩테스트에서 자주 나오는 주제이므로, 잘 연습해 두시기를 바랍니다.
추가적으로 다양한 연습 문제를 통해 실력을 쌓아가시는 것을 추천드립니다.

C# 코딩테스트 강좌, 제곱이 아닌 수 찾기

제곱이 아닌 수 찾기

이 강좌에서는 제곱이 아닌 수를 찾는 알고리즘 문제를 다루어 보겠습니다. 문제를 해결하기 위한 다양한 접근법을 익히고, 이를 통해 코딩테스트에서의 문제 해결 능력을 향상시키는 것을 목표로 합니다.

문제 설명

주어진 정수 N에 대하여, 1부터 N까지의 수 중에서 제곱 수가 아닌 수의 개수를 구하는 문제입니다. 예를 들어, N이 10 이라면 1, 4, 9가 제곱 수이므로 제곱 수가 아닌 수는 1, 2, 3, 5, 6, 7, 8, 10으로 총 8개입니다.

문제 입력

  • 정수 N (1 ≤ N ≤ 10^6)

문제 출력

  • 제곱 수가 아닌 숫자의 개수를 출력

예제

    입력 예시:
    10

    출력 예시:
    8
    

문제 접근법

이 문제를 해결하기 위한 여러 가지 방법이 있을 수 있습니다. 하지만 최적의 방법을 찾기 위해서는 제곱 수를 생성하고 이를 기반으로 제곱 수가 아닌 수의 개수를 유추해야 합니다. 제곱 수는 다음과 같이 정의됩니다:

  • X는 정수이며, Y = X * X 형태

이렇게 볼 때 1부터 N까지의 제곱 수를 찾기 위해서는 다음과 같은 로직이 필요합니다.

1. 제곱수 리스트 생성

1부터 N까지의 수에 대한 제곱수를 구하는 것은 O(√N) 복잡도를 가지므로, 제곱수를 미리 계산하여 리스트에 저장합니다.

2. 정수 N에 대한 제곱수와 비교

그 다음, 1부터 N까지의 수 중에서 제곱수 리스트에 포함된 수의 개수를 셉니다.

3. 최종 결과 도출

N에서 제곱수의 개수를 빼면 제곱이 아닌 수의 개수를 얻을 수 있습니다. 이 프로세스는 O(N)이라는 시간 복잡도를 가집니다.

코드 구현

이제 위의 논리를 바탕으로 C#으로 코드를 작성해 보겠습니다.


using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        int N = int.Parse(Console.ReadLine());
        HashSet squareNumbers = new HashSet();

        // 제곱수를 HashSet에 추가
        for (int i = 1; i * i <= N; i++)
        {
            squareNumbers.Add(i * i);
        }

        int nonSquareCount = N - squareNumbers.Count; // 제곱수가 아닌 수의 개수

        Console.WriteLine(nonSquareCount);
    }
}
    

코드 설명

  1. HashSet squareNumbers: 제곱 수를 저장할 HashSet입니다. 빠른 검색을 위해 HashSet을 사용합니다.
  2. for (int i = 1; i * i <= N; i++): 1부터 시작하여 제곱 수를 생성합니다.
  3. squareNumbers.Add(i * i): 생성된 제곱 수를 HashSet에 추가합니다.
  4. nonSquareCount = N - squareNumbers.Count: N에서 제곱 수의 개수를 빼서 제곱이 아닌 수의 개수를 구합니다.
  5. Console.WriteLine(nonSquareCount): 최종 결과를 출력합니다.

복잡도 분석

이 알고리즘의 시간 복잡도를 분석해보면:

  • 제곱수를 생성하는 부분: O(√N) (최대 √N개의 제곱수 생성)
  • 제곱수가 아닌 수의 개수를 구하는 부분: O(1) (HashSet의 크기를 가져오기 때문에 상수 시간 소요)

따라서, 전체 시간 복잡도는 O(√N)입니다. 이 알고리즘은 주어진 제약 조건 내에서 매우 효율적으로 작동합니다.

결론

이번 강좌에서는 제곱이 아닌 수를 찾는 알고리즘 문제를 해결하는 방법을 알아보았습니다. 문제를 해결하기 위한 사고 과정을 통해 문제 해결 능력을 향상시키길 바랍니다. 앞으로도 다양한 알고리즘 문제를 통해 지속적인 연습이 필요합니다.

C# 코딩테스트 강좌, 가장 빠른 버스 노선 구하기

현대 사회에서 대중교통은 중요한 역할을 하고 있으며, 특히 버스를 이용하는 경우 많은 사람들이 최적의 노선을 찾는 데 어려움을 겪곤 합니다. 이번 강좌에서는 가장 빠른 버스 노선 구하기 문제를 해결하는 과정을 살펴보겠습니다. 이 문제를 해결하기 위해 C#을 사용하여 알고리즘과 데이터 구조를 활용할 것입니다.

문제 정의

주어진 여러 개의 버스 노선과 각 노선의 소요 시간을 바탕으로 시작 지점에서 목적지까지 이동하는 가장 빠른 경로를 구하는 문제입니다.
다음과 같은 입력값을 가집니다:

  • 정점 (Vertex): 버스 정류장
  • 간선 (Edge): 버스 노선
  • 가중치 (Weight): 소요 시간

입력 예시

        5 6
        1 2 3
        1 3 2
        2 3 1
        1 4 1
        2 4 5
        3 5 1
        

여기서 첫 줄은 정점과 간선의 개수를 나타내며, 다음 각 줄은 정류장 간의 관계와 소요 시간을 보여줍니다.

문제 해결 과정

1단계: 그래프 모델링

위 문제를 해결하기 위해, 버스 정류장을 정점으로 하고, 버스 노선과 소요 시간을 간선으로 하는 그래프를 구성해야 합니다.
이를 위해, 각 정류장과 그 간선의 정보를 담은 인접 리스트를 생성합니다.

2단계: 최단 경로 알고리즘 선택

최단 경로를 찾기 위해 여러 알고리즘이 있지만, 가중치가 있는 간선이 있을 때는 Dijkstra 알고리즘을 사용하는 것이 적합합니다.
Dijkstra 알고리즘은 각 정점까지의 최단 거리를 효과적으로 계산할 수 있는 방법입니다.

3단계: C# 코드 구현

자, 이제 위 내용을 바탕으로 C# 코드를 작성해 보겠습니다. 아래 코드는 Dijkstra 알고리즘을 구현하여 주어진 그래프에서 최단 경로를 찾는 방법을 보여줍니다.

        using System;
        using System.Collections.Generic;

        class Program
        {
            static void Main(string[] args)
            {
                int vertexCount = 5; // 정점의 수
                int[,] graph = new int[vertexCount + 1, vertexCount + 1];
                for (int i = 1; i <= vertexCount; i++)
                    for (int j = 1; j <= vertexCount; j++)
                        graph[i, j] = (i == j) ? 0 : int.MaxValue;

                // 간선 추가 (소요 시간)
                graph[1, 2] = 3;
                graph[1, 3] = 2;
                graph[2, 3] = 1;
                graph[1, 4] = 1;
                graph[2, 4] = 5;
                graph[3, 5] = 1;

                // Dijkstra 알고리즘 호출
                int[] shortestPath = Dijkstra(graph, vertexCount, 1);

                // 결과 출력
                Console.WriteLine("정점 1에서 다른 정점까지의 최단 거리:");
                for (int i = 1; i <= vertexCount; i++)
                {
                    Console.WriteLine($"정점 1 -> 정점 {i}: {(shortestPath[i] == int.MaxValue ? "도달할 수 없음" : shortestPath[i].ToString())}");
                }
            }

            static int[] Dijkstra(int[,] graph, int vertexCount, int start)
            {
                bool[] visited = new bool[vertexCount + 1];
                int[] distance = new int[vertexCount + 1];

                for (int i = 1; i <= vertexCount; i++)
                {
                    distance[i] = (i == start) ? 0 : int.MaxValue;
                }

                for (int count = 0; count < vertexCount - 1; count++)
                {
                    int u = MinDistance(distance, visited, vertexCount);
                    visited[u] = true;

                    for (int v = 1; v <= vertexCount; v++)
                    {
                        if (!visited[v] && graph[u, v] != int.MaxValue &&
                            distance[u] != int.MaxValue &&
                            distance[u] + graph[u, v] < distance[v])
                        {
                            distance[v] = distance[u] + graph[u, v];
                        }
                    }
                }

                return distance;
            }

            static int MinDistance(int[] distance, bool[] visited, int vertexCount)
            {
                int min = int.MaxValue;
                int minIndex = -1;

                for (int v = 1; v <= vertexCount; v++)
                {
                    if (!visited[v] && distance[v] <= min)
                    {
                        min = distance[v];
                        minIndex = v;
                    }
                }
                return minIndex;
            }
        }
        

4단계: 시간 복잡도 분석

Dijkstra 알고리즘의 시간 복잡도는 O(V^2)입니다. 여기서 V는 정점의 수를 의미합니다.
따라서 정점의 수가 많은 경우, 더 효율적인 방법인 우선순위 큐를 사용한 개선된 Dijkstra 알고리즘을 고려할 수 있습니다.

마무리

이번 강좌에서는 C#을 사용하여 가장 빠른 버스 노선 구하기 문제를 해결하기 위해 Dijkstra 알고리즘을 구현해 보았습니다.
실제 코딩 테스트나 취업 면접에서 유사한 문제가 출제될 수 있으므로, 이 알고리즘을 충분히 이해하고 연습하는 것이 중요합니다.

앞으로도 다양한 알고리즘 문제와 해결 방법을 지속적으로 공부해 나가길 바랍니다. 추가로 궁금한 점이나 알고리즘 관련 문제는 언제든지 질문해 주세요!

C# 코딩테스트 강좌, 구간 합 구하기 1

안녕하세요! 오늘은 C#을 이용한 코딩 테스트에서 자주 출제되는 문제 중 하나인 “구간 합 구하기” 문제에 대해 다루어 보겠습니다. 이 문제를 통해 배열과 구간 합에 대한 이해도를 높이고, 이를 효율적으로 해결하기 위한 알고리즘을 배워보겠습니다.

문제 설명

구간 합 구하기 문제는 다음과 같이 정의할 수 있습니다:

정수 배열 arr와 여러 쿼리(시작 인덱스와 종료 인덱스)가 주어질 때, 각 쿼리마다 해당 구간의 합을 구하는 프로그램을 작성하시오.

입력으로는 배열의 크기 n, 배열의 원소들, 쿼리의 개수 q, 그리고 각 쿼리의 시작 인덱스 l과 종료 인덱스 r가 주어집니다.

입력 예시

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

위의 입력은 다음과 같습니다:

  • 배열의 크기: 5
  • 배열 원소: [1, 2, 3, 4, 5]
  • 쿼리 개수: 3
  • 쿼리들: (1, 3), (2, 4), (1, 5)

출력 예시

        6
        9
        15
        

출력은 각 쿼리에 대한 구간 합입니다:

  • (1, 3)의 구간 합: 1 + 2 + 3 = 6
  • (2, 4)의 구간 합: 2 + 3 + 4 = 9
  • (1, 5)의 구간 합: 1 + 2 + 3 + 4 + 5 = 15

문제 풀기

이제 이 문제를 해결하기 위한 방법을 단계별로 살펴보겠습니다. 우리는 먼저 단순한 접근법부터 시작한 후, 효율적인 방법으로 풀이를 발전시켜 보겠습니다.

1. 단순 접근법

가장 직관적인 방법은 각 쿼리의 시작 인덱스와 종료 인덱스에 맞추어 직접 합을 계산하는 것입니다. 그러나 이 방법은 비효율적일 수 있습니다. 예를 들어, 배열의 크기가 n이고 쿼리의 개수가 q일 때, 최악의 경우 O(n * q)의 시간 복잡도를 가집니다.

C#
        using System;

        class Program
        {
            static void Main()
            {
                int n = int.Parse(Console.ReadLine());
                int[] arr = Array.ConvertAll(Console.ReadLine().Split(), int.Parse);
                int q = int.Parse(Console.ReadLine());

                for (int i = 0; i < q; i++)
                {
                    var query = Array.ConvertAll(Console.ReadLine().Split(), int.Parse);
                    int l = query[0] - 1; // 0-based index
                    int r = query[1] - 1; // 0-based index

                    int sum = 0;
                    for (int j = l; j <= r; j++)
                    {
                        sum += arr[j];
                    }
                    Console.WriteLine(sum);
                }
            }
        }
        

이 코드에서는 사용자가 입력한 배열의 원소에 대해 쿼리마다 합을 계산하고 있습니다. 하지만 큰 데이터셋에서는 이 방법이 비효율적입니다.

2. 효율적인 접근법: 누적 합 배열

효율적인 방법으로는 누적 합 배열을 사용하는 것입니다. 누적 합은 배열의 각 인덱스까지의 합을 미리 계산해 두어 쿼리의 처리시간을 줄여줍니다.

즉,

C#
        sum[i] = arr[0] + arr[1] + ... + arr[i]
        

를 사용하여, 구간 합을 구하고자 할 때:

C#
        구간합 = sum[r] - sum[l-1]
        

이렇게 하면 쿼리 처리 시간이 O(1)로 줄어듭니다. 전체 시간 복잡도는 O(n + q)가 됩니다.

C#
        using System;

        class Program
        {
            static void Main()
            {
                int n = int.Parse(Console.ReadLine());
                int[] arr = Array.ConvertAll(Console.ReadLine().Split(), int.Parse);
                int q = int.Parse(Console.ReadLine());
                
                long[] sum = new long[n + 1];
                
                for (int i = 1; i <= n; i++)
                {
                    sum[i] = sum[i - 1] + arr[i - 1];
                }

                for (int i = 0; i < q; i++)
                {
                    var query = Array.ConvertAll(Console.ReadLine().Split(), int.Parse);
                    int l = query[0] - 1; // 0-based index
                    int r = query[1] - 1; // 0-based index

                    long result = sum[r + 1] - sum[l];
                    Console.WriteLine(result);
                }
            }
        }
        

이 코드에서는 먼저 누적 합 배열을 계산한 후, 각 쿼리마다 시작 인덱스와 종료 인덱스를 활용해 구간 합을 신속히 계산합니다.

결론

구간 합 구하기 문제는 코딩 테스트에서 자주 등장하는 문제로, 이를 해결하기 위해서는 누적 합 배열을 사용하는 것이 효율적임을 알 수 있습니다. 이 방법은 배열의 크기와 쿼리 개수가 클 경우 특히 유리합니다. 적절한 데이터 구조를 사용하여 알고리즘의 효율성을 높이고, 쿼리 처리 시간을 줄이는 것은 알고리즘 문제를 해결하는 데 중요한 요소입니다.

오늘 배운 내용을 바탕으로 다양한 구간 합 구하기 문제를 풀어보며 연습해 보시기 바랍니다. 감사합니다!

C# 코딩테스트 강좌, 정수를 1로 만들기

이 포스팅에서는 C#을 사용하여 주어진 정수를 1로 만드는 문제를 해결하는 방법을 상세히 설명하겠습니다. 이 문제는 코딩 테스트에서 자주 출제되는 유형 중 하나로, 문제 해결 능력과 알고리즘 설계 능력을 기르는 데 매우 유용합니다.

문제 설명

주어진 정수 x를 1로 만들기 위한 연산이 여러 가지 있습니다. 각 연산은 다음과 같습니다:

  • 1을 빼기 (x => x - 1)
  • 2로 나누기 (x => x / 2, x가 짝수일 때 가능)
  • 3으로 나누기 (x => x / 3, x가 3으로 나누어 떨어질 때 가능)

목표는 최소한의 연산을 통해 x를 1로 만드는 것입니다. 예를 들어, x = 10이라면 다음과 같은 연산을 수행할 수 있습니다:

예시:

10 → 5 (10을 2로 나누기) → 4 (5에서 1을 빼기) → 2 (4를 2로 나누기) → 1 (2에서 1을 빼기)

문제 해결 전략

이 문제를 해결하기 위한 여러 가지 접근 방법이 있지만, 가장 효율적인 방법 중 하나는 BFS(너비 우선 탐색)를 사용하는 것입니다. BFS는 최단 경로를 찾는 데 유리하며, 연산을 한 단계씩 진행하는 방식으로 문제를 해결합니다.

BFS 알고리즘 개요

BFS는 탐색할 노드를 큐에 저장하고, 가장 먼저 저장된 노드부터 차례대로 탐색하는 알고리즘입니다. 이 문제에서는 각 상태(정수)를 노드로 보고, 각 연산을 통해 도달 가능한 새로운 상태를 큐에 추가합니다. 이를 통해 1에 도달하는 최소 연산 횟수를 찾을 수 있습니다.

구현

이제 C#으로 문제를 해결하는 코드를 작성해 보겠습니다. 아래 코드는 주어진 정수 x를 1로 만드는 최소 연산 횟수를 계산하는 함수입니다.


using System;
using System.Collections.Generic;

public class Solution {
    public int MinOperations(int x) {
        // 방문한 노드를 추적하기 위한 집합
        HashSet visited = new HashSet();
        // BFS 탐색을 위한 큐
        Queue> queue = new Queue>();
        
        // 초기 상태 (x, 0) - 현재 값과 연산 횟수
        queue.Enqueue(new Tuple(x, 0));
        visited.Add(x); // 초기 노드 방문 처리
        
        while (queue.Count > 0) {
            var current = queue.Dequeue();
            int currentValue = current.Item1;
            int operationsCount = current.Item2;
            
            // 목표 상태인 1에 도달한 경우
            if (currentValue == 1) {
                return operationsCount; // 최소 연산 횟수 반환
            }
            
            // 가능한 연산 수행
            // x - 1
            if (currentValue - 1 >= 1 && !visited.Contains(currentValue - 1)) {
                visited.Add(currentValue - 1);
                queue.Enqueue(new Tuple(currentValue - 1, operationsCount + 1));
            }
            // x / 2
            if (currentValue % 2 == 0 && !visited.Contains(currentValue / 2)) {
                visited.Add(currentValue / 2);
                queue.Enqueue(new Tuple(currentValue / 2, operationsCount + 1));
            }
            // x / 3
            if (currentValue % 3 == 0 && !visited.Contains(currentValue / 3)) {
                visited.Add(currentValue / 3);
                queue.Enqueue(new Tuple(currentValue / 3, operationsCount + 1));
            }
        }
        
        return -1; // 도달할 수 없는 경우
    }
}

코드 설명

위의 C# 코드를 살펴보면, MinOperations 함수가 x를 입력받아 최소 연산 횟수를 반환합니다. 이 함수는 BFS 탐색을 통해 정수를 1로 만드는 데 필요한 모든 연산을 수행해 나갑니다.

주요 구성 요소

  • 방문 기록 (visited): 이미 탐색한 노드(정수)를 추적합니다.
  • 큐 (queue): 현재 노드 및 연산 횟수를 저장하여 BFS를 진행합니다.
  • 조건문: 각 연산에 대해 새로운 상태가 유효한지 (>= 1) 및 방문 여부를 확인합니다.

시간 복잡도 분석

이 알고리즘의 시간 복잡도는 최악의 경우 O(x)입니다. 각 정수에 대해 BFS를 수행하기 때문에, 최악의 경우 전부 탐색해야 할 수도 있습니다. 그러나 일반적으로는 빠른 성능을 보입니다.

결론

이번 포스팅에서는 C#을 사용하여 정수를 1로 만드는 문제를 BFS를 통해 해결하는 방법을 살펴보았습니다. 이 방법을 적용하면 최소 연산 횟수를 효율적으로 찾을 수 있으며, 알고리즘적 사고를 기르는 데 많은 도움이 됩니다. 추가적인 연습 문제를 푸는 것도 이 지식을 확장하는 좋은 방법이 될 것입니다.

이 문제를 바탕으로 다양한 변형 문제에 도전해 보시기 바랍니다. 항상 문제 해결에 대한 고민을 지속하여 더 깊은 이해를 가지시길 바랍니다!