Algorithm/baekjoon

[BOJ 5376] 소수를 분수로

식빵민 2022. 8. 3. 02:18
 

5376번: 소수를 분수로

유리수 분수를 소수로 나타내면, 소수점 아래 자리가 유한 개인 경우(1/8 = 0.125)와 어떤 자리에서부터 일정한 숫자가 한없이 되풀이 되는 경우(1/11 = 0.090909...)가 있다. 소수를 입력받은 뒤, 분수로

www.acmicpc.net

문제 분석

  • 제한시간 1초 : 약 1억 연산
  • 소수점 아래의 수의 범위 최대 15자리 : 소수점아래의 숫자들을 한 정수형 변수에 담을 수 없다.
  • 결과 값을 기약분수로 바꾸는 방법 : 분모와 분자의 최대 공약수를 구해 분모와 분자를 최대공약수로 나눈다.
  • 비순환소수를 분수로 바꾸는 방법 : 10^(소수점 아래의 자릿수) * 해당 소수 / 10^(소수점 아래의 자릿수)
  • 순환소수를 분수로 바꾸는 방법 :
    10^(첫 순환부까지의 자릿수) * 첫 순환부까지의 수 - 10^(첫 순환부전까지의 자릿수) * 첫 순환부 전까지의 수
    ÷
    10^(첫 순환부까지의 자릿수) - 10^(첫 순환부전까지의 자릿수)
    EX)

  • 보기에 직관적인 방법 :
    (비순환부 부터 순환부) - (비순환부) -> 분자
    순환부에 맨 뒤에서부터 9를 써주다가 , 비순환부 가 나오면 0을 쓴다. -> 분모

풀이 방식

  • 비순환소수 :
    소수점 아래의 수들을 정수형으로 바꾼다.
    빈 문자열에 '1'을 추가한다.
    문자열에 소수점 아래의 자릿수를 만큼 '0'을 추가한다.
    문자열을 정수형으로 바꾼다.
    정수로 바뀐 값 / 10...으로 이뤄진 문자열을 정수로 바꾼 값
  • 순환 소수 :
    비순환부 만 정수로 바꿔놓는다.
    순환부를 포함해서 정수로 바꾼다.
    문자열의 뒷자리부터 순환부의 자릿수만큼 '9'를 빈 문자열에 추가한다.
    그 문자열에 비순환부의 자릿수만큼 '0' 을 추가한다
    그 문자열을 정수형으로 전환한다.
    순환부 정수 - 비순환부 정수 / 9와0으로 이루어진 문자열을 정수로 바꾼 값
  • 분모와 분자의 최대공약수를 구해 ( 유클리드 호제법 이용) 기약분수로 바꾼다.
#include <iostream>

using namespace std;

string decimal;
string dnmntr;;

int n, f;
long long up, down, r, nloop;

long long gcd(long long a, long long b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n;
    while (n--) {
        dnmntr = "";
        decimal = "";
        nloop = 0;
        cin >> decimal;
        f = (int) decimal.find('(');
        if (f == -1) {
            dnmntr += "1";
            for (int j = 2; j < decimal.size(); j++) {
                dnmntr += "0";
            }
            decimal.erase(0, 2);
            up = stoll(decimal);
        } else {
            for (int j = f + 1; j < decimal.size() - 1; j++) {
                dnmntr += "9";
            }
            for (int j = 2; j < f; j++) {
                dnmntr += "0";
            }
            decimal.erase(0, 2);
            if (f > 2) nloop = stoll(decimal);
            decimal.erase(f - 2, 1);
            up = stoll(decimal) - nloop;
        }
        down = stoll(dnmntr);
        r = gcd(up, down);
        up /= r;
        down /= r;
        cout << up << '/' << down << '\n';

    }
    return 0;
}

느낀 점

  • 예전에 수학학원에서 순환 소수를 쉽게 분수로 바꾸는 방법을 배웠던 기억이 있다. 
    그 방법이 분모를 9와0으로 만드는 거였는데 그 논리를 코드로 짜는 과정이 흥미로웠다.
  • 어떻게 보면 인간이 이해하기 쉬운 (야매?요령?) 방법인데 문자열로 다루다보니 그 논리르 코드로 짜기도 편한것 같다.

시간복잡도 (테스트 케이스 하나 마다) : O(n)