소년코딩

N으로 표현

아래와 같이 5와 사칙연산만으로 12를 표현할 수 있습니다.

12 = 5 + 5 + (5 / 5) + (5 / 5) 12 = 55 / 5 + 5 / 5 12 = (55 + 5) / 5

5를 사용한 횟수는 각각 6,5,4 입니다. 그리고 이중 가장 작은 경우는 4입니다. 이처럼 숫자 N과 number가 주어질 때, N과 사칙연산만 사용해서 표현 할 수 있는 방법 중 N 사용횟수의 최솟값을 return 하도록 solution 함수를 작성하세요.

프로그래머스 알고리즘 1단계에서 꽤 어려운 문제인가보다. 이 문제를 푸는 시점에 통과한 사람이 채 100명이 되지 않는다. 99번째로 통과했다.


완전 탐색, 무식하게 풀어보자. (+ 재귀)

'무식하게 푼다(brute-force)'라는 말은 가능한 경우의 수를 일일이 나열하면서 답을 찾는 방법을 의미한다. 이렇게 가능한 방법을 전부 만들어 보는 알고리즘들을 가리켜 흔히 '완전 탐색'이라고 부른다.

이 문제를 풀 수 있는 가장 쉬운 방법은 N, NN, NNN, ... , NNNNNNNN과 사칙 연산 +, - *, /를 이용한 경우의 수 중에 가장 N을 적게 사용한 경우를 return 하면 된다.

완전 탐색을 구현할 때 유용하게 사용되는 개념으로 '재귀 함수(recursive function)'가 있다. 재귀 함수란 자신이 수행할 작업을 유사한 형태의 여러 조각으로 쪼갠 두 그중 한 조각을 수행하고, 나머지를 자기 자신을 호출해 실행하는 함수를 말한다.

#include <string>
#include <vector>
#include <cmath>

using namespace std;

vector<int> list;
int count = 0;
int minCount = 9;

// 완전 탐색 알고리즘 (Brute-Force Search)
void BFS(int N, int number)
{
    if(count >= minCount) return;
    if(list.size() >= minCount) return;
    int lastNumber = (list.size() == 0) ? 0 : list.back();

    if(lastNumber == number)
    {
        minCount = min(count, minCount);
        return;
    }

    int n = 0;
    int addCount = 0;

    for(int c = 1; c <= 10000000; c *= 10) 
    {
        addCount++;
        if(count + addCount >= minCount) continue;

        n += (N * c);

        count += addCount;

        list.push_back(lastNumber + n);
        BFS(N, number);
        list.pop_back();

        if(lastNumber - n != 0)
        {
            list.push_back(lastNumber - n);
            BFS(N, number);
            list.pop_back();
        }

        list.push_back(lastNumber * n);
        BFS(N, number);
        list.pop_back();

        if(lastNumber / n != 0)
        {
            list.push_back(lastNumber / n);
            BFS(N, number);
            list.pop_back(); 
        }

        count -= addCount;
    }

    return;
}

int solution(int N, int number) 
{    
    BFS(N, number);

    return minCount < 9 ? minCount : -1;
}

위 코드는 N부터 NNNNNNNNN까지 반복문을 돌며, 벡터에 +, -, * , / 연산의 결과값을 push, pop 하며 BFS 함수를 재귀 호출한다. 벡터의 마지막 값이 number이면 재귀를 끝낸다. 완전 탐색은 기본적으로 모든 경우의 수를 검사한다. 하지만 위 코드는 최소 사용횟수(minCount)를 비교하는 탈출 조건을 추가함으로써 연산량을 줄였다.

이 문제는 또 특이하게 효율성 검사를 하지 않는다. 위 코드의 결과는 아래와 같다.

nq1


2018/12/19 - [프로그래머스] 소수의 합, 소수 판별 알고리즘

2018/12/21 - [프로그래머스] 완주하지 못한 선수

댓글 로드 중…

블로그 정보

소년코딩 - 소년코딩

소년코딩, 자바스크립트, C++, 물리, 게임 코딩 이야기

최근에 게시된 이야기