RATIO 문제 질문드립니다.

  • Minstaa
    Minstaa

    안녕하세요. 알고스팟을 이용하다가 처음으로 질문 게시판에 글을 올리게 되었습니다!

    RATIO 문제 풀이에 있어서, 크게 아래와 같이 접근했는데요.

    플레이 횟수(N), 승리 횟수(M), 연승 횟수(k) 범위 설정

    (1<=N<=1,000,000,000), (0<=M<=N)의 입력을 가정합니다.
    k는 1~2,000,000,000의 범위를 가지도록 구현하였습니다.
    .

    현재 승률, k번 연승시 승률 비교

    현재 승률 : 문제에서의 Z는 100분율로 따지므로 floor((M/N)*100), 그리고 연승시 승률과 비교하기 위해 다시 100으로 나눠주었습니다.
    따라서 curRate = floor((M/N)*100)/100
    k번 연승시 승률 : lastRate = (M+k)/(N+k)
    이 두개의 값을 비교하여 k를 찾는 시도를 하였습니다.
    .

    조건을 만족하는 k를 얻기 위한 binary search

    start = 1, end = 2,000,000,000, k = (start+end)/2 를 초기값으로 시작, binary search를 수행하였습니다.
    k값에 대한 lastRate 계산 후, (연승시 승률 - 현재 승률) 에 따라 start, end, k 값을 재설정 하였습니다.

    • (lastRate - curRate) >= 0.01 : end = k, k = (start+end)/2
    • (lastRate - curRate) < 0.01 : start = k+1, k = (start+end)/2

    이와 같이 계산 후, 최종적으로 구해진 k를 출력하였습니다.
    .

    예외 처리

    현재 승률이 0.99 이상인 경우, N과 M의 입력값이 범위를 벗어날 경우 -1을 출력합니다.
    .

    답안 검증

    N, M 입력에 대한 결과 k는 다음과 같습니다. (N, M) : k
    1. (100000, 49999) : 2
    2. (1000000000, 980000000) : 1000000000
    3. (1000, 989) : 100
    4. (47, 43) : 3
    5. (909, 899) : 91
    6. (23456, 12345) : 185
    7. (9900, 9801) : -1
    8. (10, 10) : -1
    9. (0, 0) : -1
    10. (777, 778) : -1
    .

    질문 내용

    위의 테스트케이스 외에 랜덤하게 수를 생성하여 테스트를 진행해 보았을 때도 문제가 없는 것으로 확인했습니다.
    그리고 입력값인 N과 M에 대해서, N<1 이면 N=1, N>1,000,000,000 이면 N=1,000,000,000, M<0 이면 M=0, M>N 이면 M=N으로
    구현하고 제출한 결과도 오답으로 채점되었습니다. 드리고 싶은 질문은 다음과 같습니다.

    1. 더 다양한 테스트케이스에 대해 수행해 봐야 하는 문제인가요?
    2. 문제 해결을 위한 접근 논리에 문제가 있나요?
    3. 코드상으로 테스트케이스 외에 오답으로 판명될 부분이 존재하나요?

    질문들에 대해 도움 주시면 정말로 감사하겠습니다. ㅠㅠ

    C++ 코드
    #include <iostream>
    #include <cmath>
    
    #define MAX_COUNT 2000000000
    
    using namespace std;
    
    int main(void)
    {
        int testCase;
        cin >> testCase;
    
        while (testCase--)
        {
            long long int M, N;
            long long int start, end;
            long long int k;
            long double curRate, lastRate;
    
            start = 1;
            end = MAX_COUNT;
            k = (start + end) / 2;
    
            cin >> N >> M;
    
            if (N >= 1 && N <= MAX_COUNT / 2 && M >= 0 && M <= N)
            {
                curRate = floor((long double)M / N * 100) / 100;
                lastRate = (long double)(M + k) / (N + k);
    
                while (start != end)
                {
                    if (curRate * 100 >= 99)
                    {
                        k = -1;
                        break;
                    }
                    lastRate = (long double)(M + k) / (N + k);
    
                    if (lastRate - curRate >= 0.01)
                    {
                        end = k;
                        k = (start + end) / 2;
                    }
                    else
                    {
                        start = k + 1;
                        k = (start + end) / 2;
                    }
                }
            }
            else
                k = -1;
            printf("%lld\n", k);
        }
    
        system("pause");
        return 0;
    }
    

    9년 전
2개의 댓글이 있습니다.
  • VOCList
    VOCList

    로직은 전체적으로 훌륭한 것 같습니다. 다만 20억쯤 되는 숫자가 더해지고 빠질 경우 실수 오차로 이해 미세한 차이가 발생할 수 있을 것 같습니다.
    가능하면 실수를 사용하지 않고 정수로만 시도해보셔 주실래요?


    9년 전 link
  • Minstaa
    Minstaa

    curRate와 lastRate를 정수형으로 바꾸고 약간 수정하니 정답 처리되었어요 오오.. while문 안에 lastRate도 두번 계산하고있었다는; 이것저것 수정하다가 미처 못 지운부분이었네요. 한참 고민했던건데 이런 문제였군요. 실수의 사용으로 인해 차이가 날 수 있다는 점을 배울 수 있었습니다. 답변 정말 감사드립니다!


    9년 전 link
  • 정회원 권한이 있어야 커멘트를 다실 수 있습니다. 정회원이 되시려면 온라인 저지에서 5문제 이상을 푸시고, 가입 후 7일 이상이 지나셔야 합니다. 현재 문제를 푸셨습니다.