본문 바로가기

알고리즘/프로그래머스

N으로 표현(DP) / C++

1. 문제

2. 접근 방법

1) 문제 조건, 제한 범위 찾기 

   - N은 최대 8번 사용 가능하다 => 연산을 수행할 수는 N~NNNNNNNN까지 가능

   - 괄호 사용 가능

2) DFS로 풀기

   2-1) 종료 조건 : 원하는 수를 찾았을 때, cnt가 8을 넘어갔을 때

   2-2) 재귀 함수 호출 : 0부터 시작, 현재 수에 N부터 NNNNNN까지 사칙 연산을 각각 수행하면서 재귀함수를 호출한다. => 현재 수 (+ / - / * / 나눗셈) N,NN,NNN,...,NNNNNN

 

   2-3) 효율적으로 짜는 법 :

  • - 현재 수가 0인 경우엔 곱셈 나눗셈 수행 안함

  • - 2-2)에서 현재 수에 연산할 N의 범위는 최대 NNNNNN이다. (for문은 N부터 NNNNNN까지 6번 수행)

    NNNNNNN과 NNNNNNNN이 안되는 이유?    

 * 만약 현재 수에 연산할 수가 NNNNNNN(N이 8개)이 나오려면 현재 수는 0이어야 한다 => 0과 연산을 수행한 결과는 (0 + NNNNNNNN)과 (0 - NNNNNNNN). => 해당 값들로 다시 재귀 호출하면, cnt가 8이므로 더 이상 연산 불가 & number는 최대 5자리 수 이므로 NNNNNNNN이나 -NNNNNNNN이 될 수 없음 => return당함

 

 * 만약 현재 수에 연산할 수가 NNNNNNN(N이 7개)가 나오려면 현재 수는 0이거나 N이어야 한다.

( 현재 수가 0일 때 ) 0과 NNNNNNN을 연산을 수행한 결과인 (NNNNNNN, -NNNNNNN)로 재귀 호출 => 다시 사칙 연산을 수행하려고 보니까 cnt = 7이므로 사칙 연산 할 수 있는 수는 N만 가능 => 연산 결과는 반드시 최소 7자리 수 이상이다(NNNNNNN + N, NNNNNNN -N, NNNNNNN *N, NNNNNNN / N) => 해당 값들로 재귀 호출해도 cnt == 8이므로 더 이상 연산 불가 & number는 최대 5자리 수 이므로 일치하지 않음 => return 당함 

 

( 현재 수가 N일때 ) N과 NNNNNNN을 연산 한 결과 값으로 재귀 호출하면, cnt가 8이므로 더 이상 연산 불가 & 연산 결과 값은 항상 7자리수 이상이다. 마찬가지로 numbers는 최대 5자리 수이므로 될 수 없음 => return

 

3) DP로 풀기(bottom- up)

  3-1) DP배열 정하기 : (vector<int>) dp[i]: N을 i번 사용해서 만들 수 있는 수들의 집합 

  3-2) DP배열 초기값 : dp[i]에 각각 NN...N(i개)를 저장

  3-3) 배열에 값 업데이트 : https://gurumee92.tistory.com/164 참고함

 

프로그래머스 문제 풀이 N으로 표현

이 문제는 이시윤 강사님의 프로그래머스 강좌 "파이썬을 무기로, 코딩테스트 광탈을 면하자!"를 보고 정리한 내용입니다. 문제 URL N으로 표현 Contents 문제 지문 파악하기 강사님의 알고리즘 풀�

gurumee92.tistory.com

  - dp[i] : N을 i번 사용하여 만들 수 있는 수들의 집합 = ( dp[1] 연산 dp[i-1] ) U (dp[2] 연산 dp[i-2] ) ... U (dp[7] 연산 dp[1]) 

  - 왜 for문의 범위를 0~8까지 했는지? : 괄호 처리를 위해!!(ex. dp (55+5) / 5 와 같이 덧셈이나 뺄셈을 먼저 수행하는 경우의 수도 만들기 위해) 

  -  주의할 범위 : 0으로 나누는 경우 발생하지 않도록 함 

 

3. 조심할 것 & 배운 점

1) 문제에는 DP라고 써있는데 만약 모의고사로 나왔으면 난 DFS로 풀었을 것 같다. 

 

2) DP 배열 정의 부분에서 오래걸렸다. 처음에 dp[number]를 number을 만들기 위한 N의 최소 사용 횟수 라고 정했는데, 그렇게 하면 dp[N-i]와 dp[N]의 관계식을 구하기 어려웠다. 물론 좀 더 생각한다면 방법이 있겠지만 아직 쪼랩인 나에겐 넘나 힘든 과정이므로, 답이 안나올 것 같으면 과감히 포기하고 다른 발상을 생각해보자. 

=> dp배열에서 중요한 것이 "배열index의 의미"와 "배열에 들어있는 값"인데, 둘을 바꿔서 접근하는 연습도 하자.

 

3)  set을 써야 중복이 방지 된다! (set 쓰는 연습 하기)

 

4) dfs로 풀 때 처음 시작 값을 NNN혹은 NN 등 number의 자리수 만큼 시작했는데, 왜 굳이 0부터 시작하는 걸까? => 어차피 모든 경우를 다 돌아야 함(브루트 포스)

왜 중간 값부터 시작하면 안되는 걸까? : 최소값을 구해야 하므로 결국 모든 경우의 수를 알아내야 한다. 따라서 시작 부분은 크게 고려하지 않아도 된다.

 

5) 0에서 곱하기 나눗셈 경우를 제거해야 함

 

6)core dump 발생 시, for문의 범위를 바꿔가면서 확인 해볼 것

 

7) 기초적인 실수를 했다. tmp = tmp*10 + N해야 tmp값이 갱신되는데 tmp*10 +N값으로 dp에 넣어서 tmp값이 업데이트가 안되었다..(코드 참고)

 

8) 효율성은 DP가 더 좋은 것 같다. DP로도 풀 수 있도록 실력을 쌓쟈..

4. 코드

1) DFS

더보기
#include <vector>
#include <algorithm>
#include <iostream>
#define MAX 8
using namespace std;

int gloN, res = MAX + 1;
void dfs(int number, int curNum, int cnt) {
    if( cnt > MAX ) return;
    else if( curNum == number ){
        res = min(res, cnt);
        return;
    }
    int n = 0;
    for(int i = 1;i<= 6;i++) {
        n = n*10 + gloN; 
        dfs(number, curNum + n, cnt + i);
        dfs(number, curNum - n, cnt + i);
       if(curNum != 0) {
        dfs(number, curNum * n, cnt + i);
        dfs(number, curNum / n, cnt + i);
       }
    }
}

int solution(int N, int number) {
    gloN = N;
    dfs(number,0,0);
    return (res == 9) ? -1 : res;
}

2) DP

더보기
#include <set>
#include <algorithm>
#include <iostream>
using namespace std;

int solution(int N, int number) {
    set<int> dp[9];
    int tmp = 0;
    for(int i= 1; i<= 8; i++){ 
       tmp = tmp*10 + N;
        dp[i].insert(tmp);
    }

    for(int i = 1;i<= 8; i++) {
        for(int j = 1;j<=i; j++) {
            for( auto x : dp[j]) {
                for (auto y : dp[i-j]) {
                    dp[i].insert(x + y);
                    dp[i].insert(x - y);
                    if( x!= 0 && y != 0) {
                     dp[i].insert(x * y);
                     dp[i].insert(x / y);
                    }
                }
            }
        }
        if(dp[i].find(number) != dp[i].end() ) return i;
    }
    return -1;
}

'알고리즘 > 프로그래머스' 카테고리의 다른 글

[Lv2]구명보트(C++)  (0) 2020.10.17
[Lv2]삼각달팽이(C++)  (0) 2020.10.17
[Lev.2] 수식 최대화(C++)  (0) 2020.07.03
[Lev.3] 경주로 건설  (0) 2020.07.03
[Lev.3] 자물쇠와 열쇠(C++)  (0) 2020.07.01