티스토리 뷰

문제

세 개의 자연수 A, B, C가 주어질 때 A×B×C를 계산한 결과에 0부터 9까지 각각의 숫자가 몇 번씩 쓰였는지를 구하는 프로그램을 작성하시오.

예를 들어 A = 150, B = 266, C = 427 이라면

A × B × C = 150 × 266 × 427 = 17037300 이 되고,

계산한 결과 17037300 에는 0이 3번, 1이 1번, 3이 2번, 7이 2번 쓰였다.

입력

첫째 줄에 A, 둘째 줄에 B, 셋째 줄에 C가 주어진다. A, B, C는 모두 100보다 같거나 크고, 1,000보다 작은 자연수이다.

출력

첫째 줄에는 A×B×C의 결과에 0 이 몇 번 쓰였는지 출력한다. 마찬가지로 둘째 줄부터 열 번째 줄까지 A×B×C의 결과에 1부터 9까지의 숫자가 각각 몇 번 쓰였는지 차례로 한 줄에 하나씩 출력한다.

예제 입력 1

150
266
427

예제 출력 1

3
1
0
2
0
0
0
2
0
0

출처: Olympiad > 한국정보올림피아드 > 한국정보올림피아드시․도지역본선 > 지역본선 2006 > 초등부 2번

해설

세 수 A, B, C를 곱한 결과(result)인 숫자(int)를 문자열(char *)로 바꾸고 각 배열의 문자를 0~9까지의 문자와 비교하여 출현 횟수를 세어 출력하는 방법이 있다. 이때 숫자를 문자로 변환하기 위해서 sprintf() 함수나 _itoa()를 사용할 수 있다. 그런데 아래 코드에서는 결과(result)를 문자로 변환하지 않고 숫자로 둔 채 0~9까지의 숫자의 출현 횟수를 세어 출력하는 방식으로 풀이하였다.
우선 입력값이 세 자리의 정수이므로 A, B, C의 최대 입력은 999이다. 999가 세 번 출현했을 때 곱셈은 억 단위가 되므로 각 10의 자리의 수를 저장할 변수는 최대 9개가 있어야 한다. 따라서 배열 변수를 int arr[9];와 같이 선언한다.

세 수를 입력받아 각각을 곱한 결과를 result에 저장한다.

    for (i = 0; i < 3; ++i) {
        scanf("%d", &input_num);
        result *= input_num;
    }

result를 10자리별 숫자(유효 숫자)로 분리해 내기 위해 나누기 연산과 나머지 연산을 적절히 이용한다. 이때 1, 10, 100 … 으로 나누어야 하므로 이를 위해 제곱 함수인 pow()를 사용하였다. 이때 주의해야 할 점은 result가 10의 9승이 아닌 경우를 적절히 처리해야 한다는 점이다. 즉 result가 17037300과 같이 8자리 정수인 경우 아래 코드에 따르면 배열에 0 1 7 0 3 7 3 0 0과 같이 불필요한 0이 포함되어 출현한 숫자의 셀 때 오류가 발생하게 된다. 이를 피하기 위해 불필요한 0인 경우에는 정수 A를 대신 대입하도록 하였다. 앞서의 경우라면 배열에 A 1 7 0 3 7 3 0 0가 저장된다. 정확하게는 A의 아스키 코드가 저장되는 거지만.

    for (i = 0; i < 9; ++i) {
        tmp = pow(10, i);
        arr[i] = result / (100000000 / tmp);
        if (arr[i] == 0 && flag == 0) {
            arr[i] = 'A';
        } else {
            flag = 1;
        }
        result = result % (100000000 / tmp);
    }

완성된 배열 arr[9]의 각 원소의 값과 숫자 0~9를 비교하여 각 수의 출현 횟수를 세어 출력한다.

    for (i = 0; i <= 9; ++i) {
        tmp = 0;
        for (j = 0; j < 9; ++j) {
            if (i == arr[j]) {
                tmp++;
            }    
        }
        printf("%d\n", tmp);
    }

소스 코드 C

#include <stdio.h>    
#include <math.h>

int main(int argc, char *argv[])
{
    int arr[9], input_num, tmp;
    int i, j, flag = 0;
    int result = 1;
    for (i = 0; i < 3; ++i) {
        scanf("%d", &input_num);
        result *= input_num;
    }

    for (i = 0; i < 9; ++i) {
        tmp = pow(10, i);
        arr[i] = result / (100000000 / tmp);
        if (arr[i] == 0 && flag == 0) {
            arr[i] = 'A';
        } else {
            flag = 1;
        }
        result = result % (100000000 / tmp);
    }

    for (i = 0; i <= 9; ++i) {
        tmp = 0;
        for (j = 0; j < 9; ++j) {
            if (i == arr[j]) {
                tmp++;
            }    
        }
        printf("%d\n", tmp);
    }

    return 0;
}

추가
위 코드는 짤 때 result의 '맨 앞자리부터 유효숫자를 추출한다'라는 생각에 집중했었다. 그래서 result의 값이 될 수 있는 최댓값을 기준으로 9개의 배열을 만들고 순차적으로 유효숫자를 넣었던 것이다. 그러기 위해서 result / (100000000 / tmp);와 같은 긴 계산과 pow() 함수를 동원하였다. 하지만 본 문제를 해결하기 위해서 굳이 앞자리부터 유효숫자를 추출한 이유는 없다. 해법에서는 유효숫자가 출현한 순서를 고려의 대상이 아니므로 위와 같이 할 이유가 없는 것이다. 뒷자리부터 유효숫자를 추출해 내는 방식으로 접근하면 코드는 훨씬 간단해진다.

    for(i = 0; result < 0; ++i) {
        arr[i] = result % 10;
        result /= 10;
    }

즉 유효숫자를 추출하는 연산이 두 줄로 간단히 해결된다. 이 방식으로도 for문의 조건만 바꾸면(i = 8; result < 0 ; --i) 위와 같이 배열의 순서와 유효 숫자의 순서가 일치된다.

또한 원 코드는 유효숫자를 배열에 넣어 보관한 후 각 숫자의 출현 횟수를 바로 출력했으나 위와 같이 유효 숫자를 찾아 바로 숫자의 출현 횟수를 세는 방식으로 코드를 짤 수 있다. 이 방식으로 접근하게 되면 result의 최댓값, 즉 유효숫자의 개수를 고려하지 않아도 되어 보다 유연한 코드가 완성된다. 배열은 세고자 하는 숫자인 0~9만 고려하면 되는 것이다. 이 관점에서 코드를 재작성하면 아래와 같다.

# include <stdio.h>

int main()
{

    int i, a, j, result=1;
    int buf[10];

    /** 유효 숫자의 출현 횟수를 세기 위해 bef[10]를 0으로 초기화 **/
    for(i = 0; i < 10; i++) {
        buf[i] = 0;
    }

    /** 새 자리 정수 세 개를 입력받으면서 세 수의 곱인 result를 구한다 **/
    for(i = 0; i < 3; i++) {
        scanf("%d", &a);
        result *= a;
    }

    /** result의 뒷자리부터 유효숫자를 추출하고 해당 숫자를 배열의 첨자로 사용하여 유효숫자의 출현 횟수를 센다(buf[a] += 1) **/
    /** 이 코드의 간결함에 감동을 받았고 내 코드의 조잡함에 반성하게 되었다. 추출과 출현빈도를 두 줄의 코드로 해결하다니. **/
    for(i = 0; result > 0; i++) {
        a = result % 10;
        buf[a] += 1;
        result /= 10;
    }

    for(i = 0; i < 10; i++) {
        printf("%d\n", buf[i]);
    }

}

출처


댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함
05-19 01:37