-
프로그래머스 문제 풀이 N으로 표현24년 11월 이전/레거시-프로그래머스-코딩 테스트 고득점 kit 2019. 11. 24. 15:36반응형
이 문제는 이시윤 강사님의 프로그래머스 강좌 "파이썬을 무기로, 코딩테스트 광탈을 면하자!"를 보고 정리한 내용입니다.
문제 URL N으로 표현
Contents
- 문제 지문 파악하기
- 강사님의 알고리즘 풀이
- 구르미의 알고리즘 풀이
문제 지문 파악하기
이 문제의 요점은 사칙연산을 통해서 number를 최소로 표현하는 N의 개수를 구하는 문제입니다. 먼저 예제를 들어서 문제를 파악해보도록 하겠습니다.
12 = 5 + 5 + (5/5) + (5/5) (6개)
= 55/5 + 5/5 (5개)
= (55 + 5)/5 (4개)12는 다음과 같이 6개의 5, 5개의 5, 4개의 5를 구할 수 있습니다. 즉 12를 표현하는 5로 이루어진 수식을 만들기 위해서 필요한 5의 최소 개수는 4개라는 뜻입니다. 어떻게 풀 수 있을까요?
우리는 일단 5를 최소 1번부터 8번까지 사용할 수 있습니다. 먼저 5를 1번 사용했을 때 만들 수 있는 수를 표현해봅시다.
5를 1번 사용해서 만들 수 있는 수 :
5이번엔 5를 2번 사용해서 만들 수 있는 수를 표현해봅시다.
5를 2번 사용해서 만들 수 있는 수 :
55, 10(5+5), 0(5-5), 25(5*5), 1(5/5)55는 그냥 연달아서 숫자를 만든 경우이고, 나머지 경우는, 1번 사용했을 때 만든 수들을 이용해서 사칙연산을 통해 만들 수 있는 수들입니다. 즉 다음과 같이 표현할 수 있습니다.
5를 2번 사용해서 만들 수 있는 수 :
55, //5를 연속으로 이어붙인 수
10, 0, 25, 1 // 1번 SET 과 1번 SET 사칙연산한 결과 값들여기서 잠깐! 숫자 2를 사칙 덧셈으로 표현해봅시다.
2 = 2
= 1 + 1N으로 표현하는 것도 이 법칙을 따라갑니다. 5를 2번 사용해서 만들 수 있는 수는 결국, 2개의 5를 연속으로 이어붙인 수와, 1번 SET과 1번 SET을 사칙연산을 통해 만들어지는 수들로 표현되는 것이죠.
계속해서 5를 3번 사용해서 만들 수 있는 수들은 다음과 같이 표현할 수 있습니다. 이번에는 거꾸로 3을 덧셈으로 표현해봅시다.
3 = 3
= 1 + 2
= 2 + 1즉 5를 3번 사용하는 것은 다음과 같이 표현할 수 있습니다.
5를 3번 사용해서 만들 수 있는 수 (여기서, U는 합집합을 나타냅니다.) :
555 U
(1번 SET 과 2번 SET 사칙연산한 결과 값들) U
(2번 SET 과 1번 SET 사칙연산한 결과 값들)여기서 중요한 점은 +, * 연산은 자리가 바뀌어도 같은 값이지만, -, / 연산은 자리가 바뀌게 되면 다른 수를 나타낸다는 점입니다. 결국 이렇게 표현할 수 있는 것이죠.
계속해서 5를 4번 사용해서 만들 수 있는 수들을 살펴봅시다. 4를 덧셈으로 다음과 같이 표현할 수 있습니다.
4 = 4
= 1 + 3
= 2 + 2
= 3 + 1즉 4개의 5로 표현되는 수들은 다음과 같습니다.
5를 4번 사용해서 만들 수 있는 수 :
5555 U
(1번 SET 과 3번 SET 사칙연산한 결과 값들) U
(2번 SET 과 2번 SET 사칙연산한 결과 값들) U
(3번 SET 과 1번 SET 사칙연산한 결과 값들)이 수식들을 숫자 N에 대해서 n번 사용했을 때 표현할 수 있는 수들을 일반화시키면 다음과 같습니다
N을 n번 사용해서 만들 수 있는 수 :
N을 n번 연달아서 사용할 수 있는 수 U
N을 1번 사용했을 때 SET 과 n-1번 사용했을 때 SET을 사칙연산한 수들의 집합 U
N을 2번 사용했을 때 SET 과 n-2번 사용했을 때 SET을 사칙연산한 수들의 집합 U
... U
N을 n-1번 사용했을 때 SET 과 1번 사용했을 때 SET을 사칙연산한 수들의 집합n개의 N으로 표현할 수 있는 수들의 집합을 어떻게 구할지 아시겠나요? 이 문제는 즉, 1~8번까지 순회해서 만들어지는 수들의 집합 속에 number가 있는 최소 수를 찾아내기만하면 됩니다.
강사님의 알고리즘 풀이
위 지문 파악을 통해 만들어진 일반화를, 순서대로 표현하면 다음과 같습니다.
- [ SET x 8 ] 인 리스트를 만듭니다. 각각 N을 1개로 표현하는 수들의 집합, 2개로 표현하는 수들의 집합, ... 8개로 표현하는 수들의 집합이 저장됩니다.
- 8개의 SET에 개수만큼 N을 연달아 표현되는 수를 집어넣어줍니다.
- 숫자 N에 대해서 n개를 사용해서 표현한 수의 일반화 수식을 코드로 표현합니다.
- i에 대해서 1-8까지 순회합니다.
- j에 대해서 0-i까지 순회합니다.
- j개를 사용해서 만든 수들의 집합 s[j]를 다음과 같이 순회합니다.
- i-j-1을 사용해서 만든 수들의 집합 s[i-j-1]를 다음과 같이 순회합니다.
- op1(s[j] 순회 수)과 op2(s[i-j-1] 순회 수)를 사칙연산합니다. 나눗셈 시 op2는 0이 되면 안됩니다.
- 사칙연산한 결과 값을 집합 s[i]에 추가합니다.
- i-j-1을 사용해서 만든 수들의 집합 s[i-j-1]를 다음과 같이 순회합니다.
- j개를 사용해서 만든 수들의 집합 s[j]를 다음과 같이 순회합니다.
- 만약 number가 s[i]에 존재한다면, 반복을 멈추고 i+1번을 반환합니다.
- j에 대해서 0-i까지 순회합니다.
- 8번을 순회했음에도, number를 못찾는다면, -1을 반환합니다.
- i에 대해서 1-8까지 순회합니다.
이를 코드로 표현하면 다음과 같습니다.
def solution(N, number): # 허뎝님의 수정 피드백 -> 테스트 케이스가 바뀌면서 예외 사항을 추가해야 함. if N == number: return 1 # 1. [ SET x 8 ] 초기화 s = [ set() for x in range(8) ] # 2. 각 set마다 기본 수 "N" * i 수 초기화 for i,x in enumerate(s, start=1): x.add( int( str(N) * i ) ) # 3. n 일반화 # { # "n" * i U # 1번 set 사칙연산 n-1번 set U # 2번 set 사칙연산 n-2번 set U # ... # n-1번 set 사칙연산 1번 set, # } # number를 가장 최소로 만드는 수 구함. for i in range(1, 8): for j in range(i): for op1 in s[j]: for op2 in s[i-j-1]: s[i].add(op1 + op2) s[i].add(op1 - op2) s[i].add(op1 * op2) if op2 != 0: s[i].add(op1 // op2) if number in s[i]: answer = i + 1 break else: answer = -1 return answer
구르미의 알고리즘 풀이
제 알고리즘 역시 강사님의 알고리즘과 일치합니다. 파이썬의 syntactic sugar가 들어있긴 합니다만, 결국 같은 알고리즘입니다.
파이썬 코드
def solution(N, number): answer = -1 DP = [] for i in range(1, 9): numbers = set() numbers.add( int(str(N) * i) ) for j in range(0, i-1): for x in DP[j]: for y in DP[-j-1]: numbers.add(x + y) numbers.add(x - y) numbers.add(x * y) if y != 0: numbers.add(x // y) if number in numbers: answer = i break DP.append(numbers) return answer if __name__ == '__main__': N = 5 number = 12 result = solution(N, number) print(result)
다음은 CPP 코드입니다. CPP 코드는 강사님이 풀어주신 알고리즘을 바탕으로, 단순히 언어만 바꿨습니다.
#include <string> #include <vector> #include <set> #include <cmath> #include <iostream> using namespace std; int get_basic_number(int N, int cnt) { int res = 0; while (cnt > 0) { cnt -= 1; res += N * pow(10, cnt); } return res; } int solution(int N, int number) { // 허뎝님의 수정 피드백 -> 테스트 케이스가 바뀌면서 예외 사항을 추가해야 함. if (N == number) { return 1; } int answer = -1; const int MIN = 8; //1. [ {} x 8 ] 초기화 auto s = vector<set<int>>(MIN); // 2. 각 set마다 기본 수 "N" * i 수 초기화 int idx = 1; for (auto & x : s) { x.insert(get_basic_number(N, idx)); idx += 1; } //3. n 일반화 // { // "n" * i, // 1번 set 사칙연산 n-1번 set, // 2번 set 사칙연산 n-2번 set, // ... // n-1번 set 사칙연산 1번 set, // } // number를 가장 최소로 만드는 수 구함. for (int i=1; i<MIN; i++) { for (int j=0; j<i; j++) { for (const auto & op1 : s[j]) { for (const auto & op2 : s[i-j-1]) { s[i].insert(op1+op2); s[i].insert(op1-op2); s[i].insert(op1*op2); if (op2 != 0) s[i].insert(op1/op2); } } } if (s[i].find(number) != s[i].end()) { answer = i+1; break; } } return answer; } int main(){ int N = 5; int number = 12; auto result = solution(N, number); cout << result << endl; return 0; }
728x90'레거시 > 레거시-프로그래머스-코딩 테스트 고득점 kit' 카테고리의 다른 글
프로그래머스 문제 풀이 탑 (0) 2019.11.26 프로그래머스 문제 풀이 여행 경로 (0) 2019.11.25 프로그래머스 문제 풀이 더 맵게 (0) 2019.11.16 프로그래머스 문제 풀이 큰 수 만들기 (6) 2019.11.06 프로그래머스 문제 풀이 가장 큰 수 (2) 2019.11.06