[ Programmers Code ]/# 월간코드챌린지

[ 프로그래머스 [ 월간코드챌린지 ] 약수의 개수와 덧셈 ] (C++)

얍문 2021. 5. 17. 19:04

프로그래머스의 약수의 갯수와 덧셈(월간코드챌린지) 문제이다.

 

[ 문제풀이 ]

입력으로 주어지는 숫자의 범위에서, 약수의 갯수가 짝수인 숫자는 더하고 홀수인 숫자는 뺐을 때 그 결과값을 구해야 하는 문제이다.

 

#1. 풀이1

정말 간단하게 문제에 접근을 해보자. 우리는 Left ~ Right범위 내에 있는 각각의 숫자들이 약수의 갯수가 몇 개인지 그리고 이 갯수를 통해서 그 갯수가 홀수개인지 짝수개인지만 판단을 해주면 된다.

그럼 직접 모든 약수를 다 구해보고 그 갯수를 Count해주는 방법으로 이를 해결할 수가 있다.

약수의 갯수를 구하는 과정은 다음과 같이 구할 수 있다.

int Calculate(int x)
{
	int Cnt = 0;
	for (int i = 1; i <= x; i++)
	{
		if (x % i == 0)Cnt++;
	}
	return Cnt;
}

주어진 'x'라는 숫자의 약수를 구하는 방법이다. 1 ~ x까지 반복하면서 나누어 떨어지는 숫자가 x의 약수라는 의미이기 때문에 위와 같이 구할 수가 있다.

주어진 범위 Left ~ Right에 속해있는 모든 숫자들을 위와 같이 약수를 구해서 그 갯수를 Count하고 홀수인지 짝수인지 판단하더라도 시간초과도 발생하지 않고 충분히 pass를 받을 수 있다.

 

#2. 풀이2

지금부터는 보다 조금은 다른 풀이 방법을 이야기해보려고 한다.

본인은 이 문제를 루트를 이용해서 접근해 보았다.

우리는 사실 어떤 숫자의 약수의 갯수가 몇 개인지는  몰라도 된다. 그 갯수가 홀수개인지 짝수개인지만 알면 된다.

그럼, 약수의 갯수가 짝수개라는 것은 무엇을 의미할까 ??

바로 "제곱수에 의해 만들어지는 숫자가 아니다" 라는 것을 의미한다.

몇 가지 예를 들어보자.

숫자 '3'

'3'의 약수는 [ 1 , 3 ] 이 있고 약수의 갯수는 짝수 개이다.

숫자 '5'

'5'의 약수는 [ 1 , 5 ] 가 있고 약수의 갯수는 짝수 개이다.

숫자 '10'

'10'의 약수는 [ 1 , 2 , 5 , 10 ] 이 있고 약수의 갯수는 짝수 개이다.

위의 숫자들인 '3', '5', '10'은 어떤 수의 제곱꼴로 나타낼 수 없다는 특징이 있다.

 

반대로 약수의 갯수가 홀수인 경우를 한번 살펴보자.

숫자 '4' : 2의 제곱으로 표현이 가능하다.

'4'의 약수는 [ 1 , 2 , 4 ] 가 있고 약수의 갯수는 홀수 개이다.

숫자 '9' : 3의 제곱으로 표현이 가능하다.

'9'의 약수는 [ 1 , 3 , 9 ] 가 있고 약수의 갯수는 홀수 개이다.

숫자 '16' : 4의 제곱으로 표현이 가능하다.

'16'의 약수는 [ 1 , 2 , 4 , 8 , 16] 이 있고 약수의 갯수는 홀수 개이다.

위의 숫자들인 '4', '9', '16'은 어떤 수의 제곱꼴로 나타낼 수 있다는 특징이 있다.

 

그렇다면 왜 어떤 수의 제곱꼴로 나타낼 수 있는 숫자들은 약수의 갯수가 반드시 홀수 개일까 ??

그 이유는 "하나의 숫자로 표현이 가능" 하기 때문이다.

숫자 '10'을 생각해보자. 약수는 [ 1 , 2 , 5 , 10 ] 이 있는데, 이 약수들은 2개씩 짝을 지어야만 '10'을 표현할 수가 있다.

1과 10을 곱해서 10을 만들 수 있고, 2와 5를 곱해서 10을 만들 수 있다.

즉 ! 반드시 짝수개가 있어야만 해당 숫자를 표현할 수 있기 때문에 어떤 수의 제곱꼴로 나타낼 수 없는 숫자들은 반드시 약수의 갯수가 짝수개가 된다.

숫자 '16'을 생각해보자. 약수는 [ 1 , 2 , 4 , 8 , 16 ] 이 있는데, 이 약수들 중에서 특정 숫자는 하나의 숫자만으로도 '16'을 표현할 수가 있다.

1과 16을 곱해서 16을 만들 수 있고 , 2와 8을 곱해서 16을 만들 수 있지만, '4'같은 경우에는 4혼자만으로도 4^2으로 16을 만들 수 있다. 즉 ! 이것 때문에 어떤 수의 제곱꼴로 나타낼 수 있는 숫자들은 약수의 갯수가 반드시 홀수개라는 특징이 있다.

 

지금부터 그럼 특정 숫자를 어떤 수의 제곱꼴로 나타낼 수 있는지 어떻게 알 수 있을까 ??

본인은 이 과정에서 루트를 사용해 주었다. 지금부터 편의상 루트를 씌운 특정 숫자를 R(X) 라고 표현하겠다.

R(5)는 얼마일까 ? 아마 2.xxx 정도 될 것이다.

R(10)은 얼마일까 ? 아마 3.xxx 정도 될 것이다.

R(9)는 얼마일까 ? 정확하게 3.0000 이 될 것이다.

R(16)은 얼마일까 ? 정확하게 4.0000 이 될 것이다.

즉 ! 어떤 수의 제곱꼴로 나타낼 수 있는 숫자냐 아니냐는 루트를 씌웠을 때 뒤의 소수점이 0으로만 이루어져 있냐 아니냐로 구분을 할 수가 있다.

즉 ! 특정 숫자 K가 어떤 수의 제곱꼴로 나타낼 수 있는지 확인을 하고 싶다면...

(double)( R(K) - (int)(R(K)) )

위의 식의 값을 확인하면 될 것이다.

위의 식을 보면, R(K) 에서 R(K)의 int형 부분만 빼낸 것이다.

즉, K = 5라고 가정을 해보면,

R(K) = 2.xxxx

(int)(R(K)) = 2

가 될 것이다. 여기서 R(K) - (int)(R(K)) 를 연산하게 되면 0.xxxx 가 나오게 될 것이다.

즉 ! 위의 값이 0.0이냐 아니냐를 통해서 해당 숫자가 어떤 수의 제곱꼴로 나타낼 수 있는지 아닌지를 확인할 수 있다.

 

[ 소스코드 ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <string>
#include <vector>
#include <cmath>
 
using namespace std;
 
int solution(int left, int right)
{
    int answer = 0;
    for (int i = left; i <= right; i++)
    {
        double Sqrt_Num = sqrt(i);
        if (double(Sqrt_Num - (int)Sqrt_Num) == 0.0) answer -= i;
        else answer += i;
    }
    return answer;
}
cs