본문 바로가기

UNITY

UNITY_20231006[유니티 3D 강의, 알고리즘 문제 풀이]

알고리즘 문제 - Programmers School #47 모의고사

아래는 문제 내용이다.

더보기

Description

수포자는 수학을 포기한 사람의 준말입니다. 수포자 삼인방은 모의고사에 수학 문제를 전부 찍으려 합니다. 수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다.

1번 수포자가 찍는 방식: 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...
2번 수포자가 찍는 방식: 2, 1, 2, 3, 2, 4, 2, 5, 2, 1, 2, 3, 2, 4, 2, 5, ...
3번 수포자가 찍는 방식: 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, ...

1번 문제부터 마지막 문제까지의 정답이 순서대로 들은 배열 answers가 주어졌을 때, 가장 많은 문제를 맞힌 사람이 누구인지 배열에 담아 return 하도록 solution 함수를 작성해주세요.

 

제한 조건

  • 시험은 최대 10,000 문제로 구성되어있습니다.
  • 문제의 정답은 1, 2, 3, 4, 5중 하나입니다.
  • 가장 높은 점수를 받은 사람이 여럿일 경우, return하는 값을 오름차순 정렬해주세요.

세 수포자 모두 다른 길이의 패턴으로 문제를 찍었고, 그렇게 산출된 점수 중 최고 득점자의 그룹을 계산한다.

처음에는 아래와 같이 답을 제출했다.

더보기
using System;
using System.Collections.Generic;
using System.Linq;

namespace Algorithm_016
{
    public class TrialExam
    {
        public int[] solution(int[] problemAnswers)
        {
            int[] scores = new int[3] { 0, 0, 0 }; // 각 응시자의 점수

            int[] examinee1Pattern = new int[] { 1, 2, 3, 4, 5 }; // 응시자의 찍기 패턴
            int[] examinee2Pattern = new int[] { 2, 1, 2, 3, 2, 4, 2, 5 };
            int[] examinee3Pattern = new int[] { 3, 3, 1, 1, 2, 2, 4, 4, 5, 5 };

            int examinee1TrialIndex = 0; // 응시자의 찍기 패턴 번호
            int examinee2TrialIndex = 0; // ex) 응시자1 : 1,2,3,4,5 -> 1,2,3,~ 처음부터 다시 돈다.
            int examinee3TrialIndex = 0;
            
            int problemIndex = 0; // 문제 번호

            while (problemIndex < problemAnswers.Length) // 주어진 문제 수만큼 반복
            {
                if (examinee1TrialIndex == examinee1Pattern.Length) // 응시자가 찍는 번호가 패턴의 끝에 왔으면
                {
                    examinee1TrialIndex = 0; // 다시 번호를 0으로 돌려서 패턴의 처음부터 찍도록 한다.
                }
                if (examinee1Pattern[examinee1TrialIndex] == problemAnswers[problemIndex]) // 찍은 번호가 정답이면
                {
                    scores[0]++; // 점수 +1
                }
                examinee1TrialIndex++; // 다음 찍기 번호

                if (examinee2TrialIndex == examinee2Pattern.Length)
                {
                    examinee2TrialIndex = 0;
                }
                if (examinee2Pattern[examinee2TrialIndex] == problemAnswers[problemIndex])
                {
                    scores[1]++;
                }
                examinee2TrialIndex++;
                
                if (examinee3TrialIndex == examinee3Pattern.Length)
                {
                    examinee3TrialIndex = 0;
                }
                if (examinee3Pattern[examinee3TrialIndex] == problemAnswers[problemIndex])
                {
                    scores[2]++;
                }
                examinee3TrialIndex++;

                problemIndex++;
            }

            int highestScore = scores.Max(); // 점수 최댓값
            var rankerList = new List<int>(); // 최고 득점자 그룹

            for (int i = 0; i < scores.Length; i++) // 응시자 수만큼 반복
            {
                if (scores[i] == highestScore) // 응시자 점수가 최고점이면
                {
                    rankerList.Add(i + 1); // 그룹에 추가
                }
            }

            return rankerList.ToArray();
        }
    }
}

다시 봐도 기가 찬다. 응시자가 3명이라서 3개 경우를 전부 작성했다. 응시자가 5명만 넘어갔어도 이따위로 못했을텐데.

반복되는 로직을 반복문으로 최소화한다.

 

그렇게 개선한 답안은 아래와 같다.

더보기
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Algorithm_016
{
    public class TrialExam
    {
        public int[] solution(int[] problemAnswers)
        {
            int[] scores = new int[] { 0, 0, 0 }; // 각 응시자의 점수

            int[][] examineePatterns = new int[][] // 응시자의 찍기 패턴
            {
                new int []{ 1, 2, 3, 4, 5 },
                new int []{ 2, 1, 2, 3, 2, 4, 2, 5 },
                new int []{ 3, 3, 1, 1, 2, 2, 4, 4, 5, 5 }
            };

            int[] examineeTrialIndexes = new int[] { 0, 0, 0 }; // 응시자의 찍기 패턴 번호

            int problemIndex = 0; // 문제 번호

            while (problemIndex < problemAnswers.Length) // 주어진 문제 수만큼 반복
            {
                for (int i = 0; i < scores.Length; i++) // 응시자 수만큼 반복
                {
                    if (examineeTrialIndexes[i] == examineePatterns[i].Length) // 응시자가 찍는 번호가 패턴의 끝에 왔으면
                    {
                        examineeTrialIndexes[i] = 0; // 다시 번호를 0으로 돌려서 패턴의 처음부터 찍도록 한다.
                    }
                    if (examineePatterns[i][examineeTrialIndexes[i]] == problemAnswers[problemIndex]) // 찍은 번호가 정답이면
                    {
                        scores[i]++; // 점수 +1
                    }
                    examineeTrialIndexes[i]++; // 다음 찍기 번호
                }
                problemIndex++;
            }

            int highestScore = scores.Max(); // 점수 최댓값
            var rankerList = new List<int>(); // 최고 득점자 그룹

            for (int i = 0; i < scores.Length; i++) // 응시자 수만큼 반복
            {
                if (scores[i] == highestScore) // 응시자 점수가 최고점이면
                {
                    rankerList.Add(i + 1); // 그룹에 추가
                }
            }

            return rankerList.ToArray();
        }
    }
}

n 명의 응시자 점수, 찍기패턴(가변 배열), 찍는 번호 순서 전부 배열로 만들고, 점수 계산 과정도 n번 반복하도록 반복문을 추가했다.

그 어디에도 응시자가 3명이라는 말이 없다. 즉, 특정 찍기 패턴을 가진 응시자가 아무리 많아도 최고 득점자 그룹을 쉽게 반환할 수 있는 코드다.

 

사실 이런 식으로 만들어놓은 코드를 수정해서 다시 제출하는 일 거의 없긴 한데, 꽤 괜찮은 과정이라고 생각한다.