[백준] 2577번 : 숫자의 개수 - [C++]
https://www.acmicpc.net/problem/2577
2577번: 숫자의 개수
첫째 줄에 A, 둘째 줄에 B, 셋째 줄에 C가 주어진다. A, B, C는 모두 100보다 크거나 같고, 1,000보다 작은 자연수이다.
www.acmicpc.net
- 문제
이 번 문제는 그리 어렵지 않은 문제다.
다양한 풀이 법을 통해 접근해보도록 하자.
- 알고리즘 [접근 방법]
워낙 풀이 방법이야 다양하기 때문에 앞으로 다른 문제들을 풀어나가면서 많이 쓰게 될 방법들을 미리 터득할 겸 크게 두 가지 방식으로 접근해보고자 한다.
문제 본문은 길지만, 핵심은 하나다.
3개의 수를 곱한 수에 대해 각 자릿수들이 0~9까지 각각 몇 번 나왔는지를 출력하면 되는 문제다.
그러면 어떻게 풀이해야 할까?
바로 배열의 인덱스를 활용하여 문자의 개수를 세는 것이다.
무슨 말인가 하면 위 문제에서 예시로 나온 것은 150, 266, 427 이렇게 세 개의 숫자가 주어지고, 이 숫자들을 곱하면 17037300 이 나온다.
그러면 위 17037300 을 한 번 훑으면서 각 자리수를 배열의 인덱스로 활용하여 카운트를 해주는 것이다. 즉, 다음과 같이 말이다.
array 배열의 초기값 -> 0
1 -> index 1의 값(value)를 1 증가 ( arr[1]++ ) : arr[1] = 1
7 -> index 7의 값(value)를 1 증가 ( arr[7]++ ) : arr[7] = 1
0 -> index 0의 값(value)를 1 증가 ( arr[0]++ ) : arr[0] = 1
3 -> index 3의 값(value)를 1 증가 ( arr[3]++ ) : arr[3] = 1
7 -> index 7의 값(value)를 1 증가 ( arr[7]++ ) : arr[7] = 2
3 -> index 3의 값(value)를 1 증가 ( arr[3]++ ) : arr[1] = 2
0 -> index 0의 값(value)를 1 증가 ( arr[0]++ ) : arr[1] = 2
0 -> index 0의 값(value)를 1 증가 ( arr[0]++ ) : arr[1] = 3
그리고 배열의 인덱스 0~9까지 모두 출력해주면 되는 것이다.
이 때, 자릿수를 구하는 방법은 어떻게 접근하느냐에 따라 풀이 방법이 다양한데, 크게 두 가지로 볼 수 있다.
첫 번째는 숫자(int)를 문자열(string)으로 바꾸어 하나씩 문자를 얻어내는 방법과, 나머지와 몫을 이용하여 풀이하는 방법이 있다.
굳이 두 가지 풀이 방법을 두는 이유는 int -> string 혹은 int -> char[] 으로 바꾸는 방법은 의외로 많이 쓰이기 때문에 보편적으로는 나머지와 몫을 이용하지만, 자료형 변환을 이용하여 풀이하는 방법도 같이 소개하고자 한다.
풀이 방법은 대강 어떻게 해야하는지 알았으니 자세한 풀이는 코드를 보면서 설명하도록 하겠다.
- 3가지 방법을 사용하여 풀이한다.
앞서 설명한 방식에서 각 자릿수를 어떻게 구할지 얘기했었다. 자료형 변환과 몫과 나머지를 이용한 방법을 이용하여 풀이할 것이고, 마지막으로 보편적으로 풀이되는 방식인 몫과 나머지를 이용한 방식에 입출력을 향상시킨 방식도 같이 보고자 한다.
1. 자료형 변환 방법
2. 몫, 나머지 활용 방법
3. 몫, 나머지 활용 및 입출력 향상 방법
- 풀이
- 방법 1 : [자료형 변환 방법]
#include <iostream>
#include <string>
using namespace std;
int main(int argc, const char *argv[]) {
/*
0으로 초기화를 해야한다.
아니면 garbage value, 즉 쓰레기 값이 들어있게 된다.
이 때 0으로 초기화 하는 방법은 {} 괄호만 쳐주거나,
{0,}, {0} 방식이 있다.
*/
int count[10] = {};
int a, b, c;
cin >> a >> b >> c;
int res = a * b * c;
/*
std::to_string(val)
숫자(int, long, float, double 등)를 문자열(string)으로
변환하여 반환해주는 함수를 사용한다.
*/
string s = to_string(res);
/*
숫자 0은 문자로는 아스키 값 48에 해당하므로,
인덱스 숫자 0을 얻기 위해서는 48을 빼주어야 한다.
마찬가지로 문자 '1'을 숫자 1로 얻고자 한다면 48을 빼주어야 49 - 48 = 1로
숫자 1을 얻을 수 있다.
아스키 값이 기억이 안난다면, '0' 이렇게 문자로 빼주어도 된다.
*/
for (char ch : s) { // 문자열의 문자들을 하나씩 꺼내온다. (foreach문 활용)
// 문자를 숫자로 변환한 값의 인덱스를 1 증가시킨다.
count[ch - '0']++;
}
// 0 부터 9까지 count 배열을 출력한다. (foreach문 사용)
for (int v : count) {
cout << v << "\n";
}
return 0;
}
주석으로도 설명해놓았지만, 위와 같이 형변환을 이용하여 문자열(string)의 문자(char)를 얻어서 활용 할 수 있다.
이 때 숫자 0과 문자 '0'은 엄연히 다른 값이다.
그렇기 때문에 문자로 활용할 경우 실제 숫자 값을 얻고자 할 때는 '0' (=48) 만큼 빼주어야 실제 값을 얻을 수 있다.
char도 콘솔로 나오는 것은 문자로 나오지만, 실제 값은 숫자로 이루어져 있기 때문에 위와 같이 '0'으로도 뺄 수 있어 아스키 값이 기억이 안난다면 위와 같이 문자로 직접 뺄 수 있다.
아스키 코드표는 아래와 같이 있으니 참고하시길 바라며 외워두면 좋은 문자는 '0'과 'a', 'A' 정도가 되겠다.
- 방법 2 : [몫, 나머지 활용 방법]
앞서서는 문자열을 활용하여 각 문자의 값(아스키 값)을 이용했다면, 이 번에는 몫과 나머지를 이용하는 방식이다.
아마 이 방법이 가장 보편적일 것이다.
#include <iostream>
using namespace std;
int main(int argc, const char *argv[]) {
/*
0으로 초기화를 해야한다.
아니면 garbage value, 즉 쓰레기 값이 들어있게 된다.
이 때 0으로 초기화 하는 방법은 {} 괄호만 쳐주거나,
{0,}, {0} 방식이 있다.
*/
int count[10] = {};
int a, b, c;
cin >> a >> b >> c;
int res = a * b * c;
// 곱한 값이 0이 될 때 까지 반복
while(res != 0) {
count[res % 10]++; // res에서 나머지 10을 통해 자릿수를 얻어 인덱스로 활용
res /= 10; // 매 회 자릿수를 줄이기 위해 10을 나눈다.
}
// 0 부터 9까지 count 배열을 출력한다. (foreach문 사용)
for (int v : count) {
cout << v << "\n";
}
return 0;
}
이 방법이 가장 쉬울 것이다.
17037300 이 있을 때
17037300 % 10 = 0 → count[0] ++ (index 0의 값 1 증가)
위와 같이 값을 증가시켰다면, 다음 자릿수를 계산하기 위해 10을 나누어 다음과 같이 한다.
17037300 / 10 = 1703730
그 다음 위 과정을 반복하는 것이다.
- 방법 3 : [몫, 나머지 활용 및 입출력 향상 방법]
이 방법은 필자의 다른 포스팅을 보았다면 알겠지만, 입출력 방법을 조금 더 향상시키는 방법을 추가한 것이다. (사실 입출력 데이터가 그리 많진 않아 크게 차이가 없긴 할 것이다.)
알고리즘 자체는 방법 2와 같기 때문에 그리 다르지는 않을 것이다.
#include <iostream>
using namespace std;
int main(int argc, const char *argv[]) {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
/*
0으로 초기화를 해야한다.
아니면 garbage value, 즉 쓰레기 값이 들어있게 된다.
이 때 0으로 초기화 하는 방법은 {} 괄호만 쳐주거나,
{0,}, {0} 방식이 있다.
*/
int count[10] = {};
int a, b, c;
cin >> a >> b >> c;
int res = a * b * c;
// 곱한 값이 0이 될 때 까지 반복
while(res != 0) {
count[res % 10]++; // res에서 나머지 10을 통해 자릿수를 얻어 인덱스로 활용
res /= 10; // 매 회 자릿수를 줄이기 위해 10을 나눈다.
}
// 0 부터 9까지 count 배열을 출력한다. (foreach문 사용)
for (int v : count) {
cout << v << "\n";
}
return 0;
}
- 성능
채점 번호 : 33794365 - 방법 3 : 몫, 나머지 활용 및 입출력 향상 방법
채점 번호 : 33794356 - 방법 2 : 몫, 나머지 활용 방법
채점 번호 : 33794345 - 방법 1 : 자료형 변환 방법
결과에서 차이를 보이지는 않는다.
- 정리
이 번 문제 또한 어려운 점은 없었을 것이다.
다만, 필자가 int -> string 변환을 사용하여 풀이 한 것을 보여준 이유는 이 방법이 의외로 쓰일 곳이 많기 때문이다.
혹은 string -> int 로 변환하는 경우도 있는데, 그럴 때는 stoi() 를 쓰면 된다.
stoi가 string to int를 줄인 말이다.
그 외에도 stol(string to long), stoll(string to long long), stof(string to float), stod(string to float), stoui(string to unsigned int) 등도 지원하니 알아두면 좋을 것이다.
'C++ - 백준 [BAEK JOON] > 기타 문제' 카테고리의 다른 글
[백준] 2742번 : 기찍 N - [C++] (0) | 2021.04.12 |
---|---|
[백준] 2741번 : N 찍기 - [C++] (0) | 2021.04.06 |
[백준] 10718번 : We love kriii - [C++] (0) | 2021.02.04 |