💻STUDY/BOJ

[BOJ] 1629. 곱셈 (C++) (분할정복)

coldNoodlePigeon 2022. 7. 24.
  • 코딩초보
  • 분할정복 

1629. 곱셈 (실버1) 

 

문제

자연수 A를 B번 곱한 수를 알고 싶다. 단 구하려는 수가 매우 커질 수 있으므로 이를 C로 나눈 나머지를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 A, B, C가 빈 칸을 사이에 두고 순서대로 주어진다. A, B, C는 모두 2,147,483,647 이하의 자연수이다.

출력

첫째 줄에 A를 B번 곱한 수를 C로 나눈 나머지를 출력한다.

 

 

#include <iostream>

using namespace std;

long long a, b, c,tmp; 

//b가 짝수일 경우: a^b = a^(b/2)*a^(b/2) 
//b가 홀수일 경우: a^b = a^(b/2)*a^(b/2 +1) 
//long long 은 9,223,372,036,854,775,807까지 표현 가능. 사실상 무제한의 정수 범위. 

long long pow(long long b) {
	if (b == 0) return 1; 
	if (b == 1) return a % c; 

	tmp = pow(b / 2) % c; //중복 재귀호출 방지 

	if (b % 2 == 0) return tmp * tmp%c; 
	return tmp * tmp%c * a % c; 
}


int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> a >> b >> c; 
	cout << pow(b); 

	
	return 0;
}

 

분할정복 알고리즘을 이용하는 문제다. 

처음에 감이 안잡혀서 이분의 코드( https://velog.io/@junttang/BOJ-1629-%EA%B3%B1%EC%85%88-%ED%95%B4%EA%B2%B0-%EC%A0%84%EB%9E%B5-C) 를 참고하여 공부했다. 다음에 비슷한 분할정복 문제가 나오면 또 잘 풀 수 있어야 할텐데..! 

 

분할 정복이란, 함수를 재귀적으로 호출하여 큰 문제를 작은 문제들로 분할하여 solve하는 알고리즘이다. 위와 같이 거듭제곱과 같은 문제를 해결할 때 많이 사용하는 알고리즘이다. 

 

 

위와 같이 큰 수를 거듭제곱할때, c로 나눈 나머지를 구해야하므로 함수에서 거듭제곱을 다 한 후 c로 나누지 말고, 호출할때 c로 나눈 나머지를 가지고 계산을 하는 것이 더 효율적이다. 

 

이때 b가 짝/홀일때의 경우를 나눠서 문제를 쪼갤 수 있다. 

 

b가 짝수일 경우: a^b = a^(b/2)*a^(b/2)

b가 홀수일 경우: a^b = a^(b/2)*a^(b/2 +1) 

로 표현 가능하다. 

 

 

따라서 b가 1이 될때까지 문제를 쪼개는 base case로 생각해야한다. 

 

여기서 위의 블로그 글에서 참고했듯이, tmp를 사용하여 중복 재귀호출을 방지해야한다. (중복호출은 시간초과를 일으킨다!) 

 

tmp를 통해 중복호출을 방지하고 그 값을 기억하도록 한다. 그리고 b가 짝수일 경우 tmp*tmp %c를 return하고, b가 홀수일 경우 tmp*tmp%c*a%c를 해준다. 여기서 c를 나눠주는 이유는 (A*B) % C = (A%C * B%C) %C의 성질을 사용했다. 

'💻STUDY > BOJ' 카테고리의 다른 글

[BOJ] 10816. 숫자 카드2 (C++)  (0) 2022.07.23
[BOJ] 11651. 좌표 정렬하기2 (C++)  (0) 2022.07.09
[BOJ] 15650. N과 M(2) (C++)  (0) 2022.07.08
[BOJ] 15649. N과 M (1) (C++)  (0) 2022.07.07
[BOJ] 2606. 바이러스 (C++)  (0) 2022.07.03

댓글