View

[백준] 1629 - 곱셈

sm_amoled 2022. 1. 4. 20:17
300x250

문제링크

https://www.acmicpc.net/problem/1629

문제

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

입력

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

출력

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

조건

  • 시간 제한 : 0.5s
  • 메모리 제한 : 128MB

해설

굳이 A^B의 전체 수를 구한 다음 C로 나누어 나머지를 구하지 말고, 계속해서 나머지를 이용해 그 다음 나머지를 구해나가면 작은 메모리 안에서 해결이 가능하다.

A B C 가 10 11 12라고 하면 result는 A^B % C 와 같다. 

A   = (C x A') + K1
A^2 = ((C x A') + K1)^2 = C x ( ... ) + K1^2
→ 필요한 건 결국 나머지 값끼리의 곱 (다른 값들은 C의 배수)

A^P에서의 C로 나눈 나머지를 Kp 라고 한다면 
K11 → ((K5 * K5) * K1) % C
K5  → ((K2 * K2) * K1) % C
K2  → (K1 * K1) % C
K1  → A % C

 

 

 

위와 같은 방법으로 지수가 1일 때부터 필요한 값들을 만들면서 올라오면 필요한 나머지를 구할 수 있다. 여기에서 P가 홀수일 때는 3개의 나머지를 곱하게 되는데, 만약 C 값이 2^32에 가까워서, 곱해줄 나머지 값들도 2^32에 가깝다면 3개의 나머지를 곱했을 때 2^96에 가까운 값이 나오게 되므로 long long 자료형을 사용해도 정상적으로 값을 담을 수 없다. 따라서 우리가 필요한 값만 사용해주면 되므로, ((K^P’ * K^P’ % C) * K1) % C 와 같은 식을 사용하여 미리 나머지를 구해 long long 자료형의 범위인 2^64 범위 내에 담을 수 있게 해준다.

 

풀이

B에 대해 B/2일때의 값을 temp에 저장하여 B일때의 나머지를 반환해준다. 여기에서 B가 짝수라면 B/2일때의 값을 그대로 두 번 곱해 나머지를 구해주면 되고, B가 홀수라면 위에서 구한 나머지에 A를 한 번 더 곱해 나머지를 구해준다.

long long remains(int A, int B, int C) {
    if(B == 1) return A % C;
    
    long long temp = remains(A, B/2, C);
    
    if(B % 2 == 0) {
        return (temp*temp) % C;
    } else {
        return ((temp*temp) % C * A) % C;
    }
}

 

A, B, C를 입력받은 후 아래 코드를 실행시키면 원하는 결과를 얻을 수 있다.

cout << remains(A, B, C);

 


코멘트

생각보다 어려웠다. 이런 풀이방법도 분할 정복에 들어가는구나...

 


코드

#include <iostream>

using namespace std;

long long remains(int A, int B, int C) {
    if(B == 1) return A % C;
    
    long long temp = remains(A, B/2, C);
    
    if(B % 2 == 0) {
        return (temp*temp) % C;
    } else {
        return ((temp*temp) % C * A) % C;
    }
}

int main() {
    int A, B, C;
    cin >> A >> B >> C;
    
    cout << remains(A, B, C);
        
    return 0;
}

문제 해결에 대한 고민

처음에는 B를 찾아갈 때 까지 나머지에 계속 A를 곱하면서 B의 카운트를 1씩 올리려고 했었다. 이 경우의 시간복잡도는 O(B). 근데 B가 2^32의 값을 가지면서 시간초과가 뜨더라. 그래서 생각해낸 방법이 위에처럼 반으로 접으면서 해결하는 방법이고, O(log B)의 시간복잡도를 갖는다. 이런 것도 분할정복에 들어가는군.

#include <iostream>

using namespace std;

int main() {
    int A, B, C;
    cin >> A >> B >> C;
    
    int remain = 1;
    
    for(int i = 0; i < B; i++) {
        remain = remain * A % C;
    }
    
    cout << remain;
        
    return 0;
}
320x100

'Develop > 알고리즘' 카테고리의 다른 글

[백준] 2836 - 수상 택시  (0) 2022.01.04
[백준] 3392 - 화성지도  (0) 2022.01.04
[백준] 2170 - 선 긋기  (0) 2022.01.04
[백준] 1934 - 최소공배수  (0) 2022.01.04
[백준] 1676 - 팩토리얼 0의 개수  (0) 2022.01.04
Share Link
reply
반응형
«   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