코딩테스트/백준

2661번 좋은 수열

Cadi 2025. 5. 29. 00:30

코딩테스트 : 2661번 좋은 수열

시간 제한메모리 제한제출정답맞힌 사람정답 비율
1 초 128 MB 17392 8622 6641 50.242%

문제

숫자 1, 2, 3으로만 이루어지는 수열이 있다. 임의의 길이의 인접한 두 개의 부분 수열이 동일한 것이 있으면, 그 수열을 나쁜 수열이라고 부른다. 그렇지 않은 수열은 좋은 수열이다.

다음은 나쁜 수열의 예이다.

  • 33
  • 32121323
  • 123123213

다음은 좋은 수열의 예이다.

  • 2
  • 32
  • 32123
  • 1232123

길이가 N인 좋은 수열들을 N자리의 정수로 보아 그중 가장 작은 수를 나타내는 수열을 구하는 프로그램을 작성하라. 예를 들면, 1213121과 2123212는 모두 좋은 수열이지만 그 중에서 작은 수를 나타내는 수열은 1213121이다.

입력

입력은 숫자 N하나로 이루어진다. N은 1 이상 80 이하이다.

출력

첫 번째 줄에 1, 2, 3으로만 이루어져 있는 길이가 N인 좋은 수열들 중에서 가장 작은 수를 나타내는 수열만 출력한다. 수열을 이루는 1, 2, 3들 사이에는 빈칸을 두지 않는다.

 

 처음에는 가능한 모든 수열을 생성한 후 검사하는 완전탐색 방식도 고려했으나, 이는 효율적인 방법이 아니라고 판단했다.

 수열의 길이가 최대 80이라면 경우의 수는 3⁸⁰으로 매우 크다.따라서 가능한 수열을 모두 생성해보는 방식 대신, 수열을 왼쪽부터 하나씩 쌓아가면서 가능한 숫자(1, 2, 3)를 작은 수부터 시도하고, 좋은 수열인지 확인하는 방식으로 탐색을 진행하기로 했다.

using System.Text;

public class BackJoon
{
    private static int n;
    private static bool isEnd = false;
    public static void Main()
    {
        //아이디어 : 1을 넣을 수 있으면 무조건 1을 넣고, 아니면 2 , 아니면 3을 넣는데 앞의 모든 수열들을 검사해야 한다. 
        n = int.Parse(Console.ReadLine());
        DFS("");
    }


    public static void DFS(string current)
    {
        if(isEnd) return;
        if (current.Length == n)
        {
            isEnd = true;
            Console.WriteLine(current);
            return;
        }

        foreach (char c in new char[] { '1', '2', '3' })
        {
            string next = current + c;

            if (isGood(next))
            {
                DFS(next);
            }
        } 
    }

    public static bool isGood(string next)
    {
        int len = next.Length;

        for (int i = 1; i <= len / 2; i++)
        {
            int start1 = len - i;
            int start2 = len - 2 * i;

            if (start2 < 0) continue; // 인덱스 오류 방지

            string part1 = next.Substring(start1, i);
            string part2 = next.Substring(start2, i);

            if (part1.Equals(part2))
                return false;
        }

        return true;
    }


}

 

 가장 헷갈렸던 부분은 "수열의 어떤 구간을 기준으로 얼마나 길게 비교해야 반복되는 패턴을 잡아낼 수 있는가"였다.

 

 

 처음에는 문제를 잘못 이해해서, 새로 숫자 2를 넣는다고 하면 수열 앞부분에서 등장하는 2를 기준으로 부분 수열을 잡고, 동일한 길이만큼 뒷부분에서 잘라 비교하는 식으로 검사하려 했다. 그러나 이 방식은 연속된 부분 수열을 비교하는 문제 조건과는 맞지 않았다.

 다만 문제를 다시 읽어보니 연속되지 않으면 되는 것이라 다른 방식으로 바꿨다.