본문 바로가기
프로그래머스/3단계

(Python/LV3) [PCCP 기출문제] 4번/ 수식 복원하기

by windy7271 2024. 10. 11.
728x90
반응형

 

문제 설명

당신은 덧셈 혹은 뺄셈 수식이 여러 개 적힌 고대 문명의 유물을 찾았습니다. 이 수식들을 관찰하던 당신은 이 문명이 사용하던 진법 체계가 10진법이 아니라는 것을 알아냈습니다. (2 ~ 9진법 중 하나입니다.)

수식들 중 몇 개의 수식은 결괏값이 지워져 있으며, 당신은 이 문명이 사용하던 진법에 맞도록 지워진 결괏값을 채워 넣으려 합니다.

다음은 그 예시입니다.

<수식>

14 + 3 = 17

13 - 6 = X

51 - 5 = 44

  • X로 표시된 부분이 지워진 결괏값입니다.

51 - 5 = 44에서 이 문명이 사용하던 진법이 8진법임을 알 수 있습니다. 따라서 13 - 6 = X의 지워진 결괏값을 채워 넣으면 13 - 6 = 5가 됩니다.

다음은 또 다른 예시입니다.

<수식>

1 + 1 = 2

1 + 3 = 4

1 + 5 = X

1 + 2 = X

주어진 수식들에서 이 문명에서 사용한 진법이 6 ~ 9진법 중 하나임을 알 수 있습니다.
1 + 5 = X의 결괏값은 6진법일 때 10, 7 ~ 9진법일 때 6이 됩니다. 이와 같이 결괏값이 불확실한 수식은 ?를 사용해 1 + 5 = ?와 같이 결괏값을 채워 넣습니다.
1 + 2 = X의 결괏값은 6 ~ 9진법에서 모두 3으로 같습니다. 따라서 1 + 2 = X의 지워진 결괏값을 채워 넣으면 1 + 2 = 3이 됩니다.

덧셈 혹은 뺄셈 수식들이 담긴 1차원 문자열 배열 expressions가 매개변수로 주어집니다. 이때 결괏값이 지워진 수식들의 결괏값을 채워 넣어 순서대로 문자열 배열에 담아 return 하도록 solution 함수를 완성해 주세요.

 

제한사항

  • 2 ≤ expressions의 길이 ≤ 100

 

입출력 예

expressions result
["14 + 3 = 17", "13 - 6 = X", "51 - 5 = 44"] ["13 - 6 = 5"]
["1 + 1 = 2", "1 + 3 = 4", "1 + 5 = X", "1 + 2 = X"] ["1 + 5 = ?", "1 + 2 = 3"]
["10 - 2 = X", "30 + 31 = 101", "3 + 3 = X", "33 + 33 = X"] ["10 - 2 = 4", "3 + 3 = 10", "33 + 33 = 110"]
["2 - 1 = 1", "2 + 2 = X", "7 + 4 = X", "5 - 5 = X"] ["2 + 2 = 4", "7 + 4 = ?", "5 - 5 = 0"]
["2 - 1 = 1", "2 + 2 = X", "7 + 4 = X", "8 + 4 = X"] ["2 + 2 = 4", "7 + 4 = 12", "8 + 4 = 13"]

입출력 예 설명

입출력 예 #1

문제 예시와 같습니다.

입출력 예 #2

문제 예시와 같습니다.

입출력 예 #3

30 + 31 = 101에서 이 문명이 사용하던 진법이 6진법임을 알 수 있습니다. 따라서 10 - 2 = X, 3 + 3 = X, 33 + 33 = X의 지워진 결괏값을 채워 넣으면 10 - 2 = 4, 3 + 3 = 10, 33 + 33 = 110이 됩니다.

따라서 ["10 - 2 = 4", "3 + 3 = 10", "33 + 33 = 110"]을 return 해야 합니다.

입출력 예 #4

수식에 등장하는 숫자들을 통해 이 문명이 사용하던 진법이 8진법 혹은 9진법임을 알 수 있습니다. 2 + 2 = X와 5 - 5 = X의 지워진 결괏값을 채워 넣으면 8진법, 9진법에 관계없이 2 + 2 = 4, 5 - 5 = 0이 됩니다. 7 + 4 = X의 결괏값은 불확실하므로 지워진 결괏값을 채워 넣으면 7 + 4 = ?가 됩니다.

따라서 ["2 + 2 = 4", "7 + 4 = ?", "5 - 5 = 0"]을 return 해야 합니다.

입출력 예 #5

네 번째 예시와 같지만 5 - 5 = X가 8 + 4 = X로 바뀌었습니다. 이 문명이 사용하던 진법이 9진법임을 알 수 있으므로 7 + 4 = X와 8 + 4 = X의 지워진 결괏값을 채워 넣으면 7 + 4 = 12, 8 + 4 = 13이 됩니다.

따라서 ["2 + 2 = 4", "7 + 4 = 12", "8 + 4 = 13"] return 해야 합니다.

 

풀이 : 

이 문제는 구현 문제이다.

 

일단 최소한을 돌기 위해서 주어진 식에서 가장 큰 숫자를 찾아주었고

그 다음 숫자부터 진법이 들어가게한다.

 

int 함수를 사용하면 int("10",진수) 넣어주면 저절로 계산이 된다.

 

1. 73.9 점

from collections import defaultdict


# 10진수 값을 base 진법으로 변환하는 함수
def to_base(n, base):
    if n == 0:
        return "0"
    digits = []
    while n > 0:
        digits.append(str(n % base))
        n //= base
    return ''.join(digits[::-1])


def valid_result(base, expression):
    # expression을 공백으로 분리
    ls = expression.split(" ")
    a = int(ls[0], base)  # 첫 번째 숫자
    b = int(ls[2], base)  # 두 번째 숫자
    operator = ls[1]  # 연산자
    # 결과 값이 X인 경우 (X 값을 구해야 하는 상황)
    if ls[4] == "X":
        if operator == "+":
            result = a + b
        elif operator == "-":
            result = a - b
        return to_base(result, base)  # 계산 결과를 base 진법으로 변환하여 반환


    # 결과 값이 주어진 경우 (정답 검증)
    else:
        c = int(ls[4], base)  # 결과 값

        if operator == "+" and a + b == c:
            return base
        elif operator == "-" and a - b == c:
            return base
    return False


def solution(expressions):
    number_stk = []
    calculate_stk = []
    number = ""
    incorrect_expression = []
    correct_expression = []
    # 시작 할 base 값 구하기

    for expression in expressions:
        if "X" in expression:
            incorrect_expression.append(expression)
        else:
            correct_expression.append(expression)
        express, res = expression.replace(" ", "").split("=")

        if res != "X":
            number += res
        for i in express:
            if i == "+" or i == "-":
                calculate_stk.append(i)
            else:
                number_stk.append(i)
                number += i
    number = sorted(number, key=lambda x: -int(x[0]))
    start_base = int(number[0]) + 1

    # 뭐가 될지 찾아야함.
    possible_base = defaultdict(list)
    for expression in correct_expression:
        for base in range(start_base, 10):
            valid = valid_result(base, expression)
            if valid:
                possible_base[expression].append(valid_result(base, expression))
    if len(possible_base) >= 1:
        common_bases = set(possible_base[next(iter(possible_base))])  # 첫 번째 키의 값을 기준으로 초기화
        for bases in possible_base.values():
            common_bases.intersection_update(bases)  # 교집합 업데이트

        set_possible_base = sorted(list(common_bases))

    answer = []
    for incorrect_express in incorrect_expression:
        sets = set()
        for possible_base in set_possible_base:
            sets.add(valid_result(possible_base, incorrect_express))
        temp = str(sets.pop()) if len(sets) == 1 else '?'
        answer.append(incorrect_express[:-1] + temp)
    return answer


print(solution(["14 + 3 = 18"]))
# print(solution(["1 + 1 = 2", "1 + 3 = 4", "1 + 5 = X", "1 + 2 = X"]))
# print(solution(["10 - 2 = X", "30 + 31 = 101", "3 + 3 = X", "33 + 33 = X"]))
# print(solution(["2 - 1 = 1", "2 + 2 = X", "7 + 4 = X", "5 - 5 = X"]))
# print(solution(["2 - 1 = 1", "2 + 2 = X", "7 + 4 = X", "8 + 4 = X"]))

 

뒤에 6~7개 정도가 시간초과가 나는데 

생각해보니 길이가 0 일때 런타임 에러가 터지는 것 같다.

 

 

# 10진수 값을 base 진법으로 변환하는 함수
def to_base(n, base):
    if n == 0:
        return "0"
    digits = []
    while n > 0:
        digits.append(str(n % base))
        n //= base
    return ''.join(digits[::-1])


def valid_result(base, expression):
    # expression을 공백으로 분리
    ls = expression.split(" ")
    a = int(ls[0], base)  # 첫 번째 숫자
    b = int(ls[2], base)  # 두 번째 숫자
    operator = ls[1]  # 연산자
    # 결과 값이 X인 경우 (X 값을 구해야 하는 상황)
    if ls[4] == "X":
        if operator == "+":
            result = a + b
        elif operator == "-":
            result = a - b
        return to_base(result, base)  # 계산 결과를 base 진법으로 변환하여 반환

    # 결과 값이 주어진 경우 (정답 검증)
    else:
        c = int(ls[4], base)  # 결과 값
        if operator == "+" and a + b == c:
            return base
        elif operator == "-" and a - b == c:
            return base
    return False


def solution(expressions):
    number_stk = []
    calculate_stk = []
    number = ""
    incorrect_expression = []
    correct_expression = []
    # 시작 할 base 값 구하기

    for expression in expressions:
        if "X" in expression:
            incorrect_expression.append(expression)
        else:
            correct_expression.append(expression)
        express, res = expression.replace(" ", "").split("=")

        if res != "X":
            number += res
        for i in express:
            if i == "+" or i == "-":
                calculate_stk.append(i)
            else:
                number_stk.append(i)
                number += i
    number = sorted(number, key=lambda x: -int(x[0]))
    start_base = int(number[0]) + 1

    # 뭐가 될지 찾아야함.
    answer_list = []
    for n in range(start_base, 10):
        temp = 1
        for correct in correct_expression:
            x = valid_result(n, correct)
            if not x:
                temp = 0
        if temp:
            answer_list.append(n)

    answer_list = list(set(answer_list))
    answer = []
    for incorrect_express in incorrect_expression:
        sets = set()
        for possible_base in answer_list:
            sets.add(valid_result(possible_base, incorrect_express))
        temp = str(sets.pop()) if len(sets) == 1 else '?'
        answer.append(incorrect_express[:-1] + temp)
    return answer

 

 

answer_list 부분을 바꾸어 주었다.

 

원래는 각 식 마다 가능한 숫자들을 디폴트 딕에 담았지만 이번에는 모든 식에서 가능한 숫자들을 담는 식으로 했고.

set과 list로 범위를 줄여주었다.

 

반응형

댓글