문제
크기가 N*N인 행렬 A가 주어진다. 이때, A의 B제곱을 구하는 프로그램을 작성하시오. 수가 매우 커질 수 있으니, A^B의 각 원소를 1,000으로 나눈 나머지를 출력한다.
입력
첫째 줄에 행렬의 크기 N과 B가 주어진다. (2 ≤ N ≤ 5, 1 ≤ B ≤ 100,000,000,000)
둘째 줄부터 N개의 줄에 행렬의 각 원소가 주어진다. 행렬의 각 원소는 1,000보다 작거나 같은 자연수 또는 0이다.
출력
첫째 줄부터 N개의 줄에 걸쳐 행렬 A를 B제곱한 결과를 출력한다.
내가 푼 풀이
접근방법: 분할정복
첫번째로 행렬의 곱셈을 먼저 구현한다. (for 3중반복문)
거듭제곱을 그대로 곱하면 1000억번 연산해야하므로 시간초과가 무조건 난다.
여기서 거듭제곱을 지수법칙을 적용해 분할정복 형식으로 곱한다.
$ A^{B} = A^{\frac{B}{2}} \times A^{\frac{B}{2}} $ 이 성립하므로 지수가 주어지면 1이 될때까지 반으로 분할한다.
지수가 홀수:
$A^{B} = A^{\frac{B}{2}} \times A^{\frac{B}{2}} \times A $
지수가 짝수:
$A^{B} = A^{\frac{B}{2}} \times A^{\frac{B}{2}}$
1까지 분할이 됬다면 두 행렬을 곱한다(정복)
코드로 구현하면 다음과 같다.
import Foundation
// 입력받기
var input = readLine()!.split(separator: " ").map{Int(String($0))!}
let N = input[0], B = input[1]
var arr = [[Int]]()
for i in 0..<N {
arr.append(readLine()!.split(separator: " ").map{Int(String($0))!})
}
// 분할 정복
func divide(_ N: [[Int]],_ B: Int) -> [[Int]]{
// 지수가 1이 될때 까지 분할
if B == 1 {
return N
}
// 지수를 반으로 분할
let m = divide(N, B/2)
// 지수가 짝수인경우 나머지가 없으므로 나눈 두 행렬을 곱
if B % 2 == 0 {
return multi(m, m)
} else {
// 지수가 홀수라면 나머지1이 존재하므로 나눈 두 행렬과 지수가 1인 행렬 곱
return multi(multi(m, m), N)
}
}
//행렬 곱
func multi(_ x: [[Int]],_ y: [[Int]]) -> [[Int]] {
var result = Array(repeating: Array(repeating: 0, count: N), count: N)
for i in 0..<N {
for j in 0..<N {
for k in 0..<N {
result[i][j] += ((x[i][k] * y[k][j]))
}
result[i][j] %= 1000
}
}
return result
}
// 지수가 1이고, 행렬의 원소가 1000 이상이라면 출력은 해당원소를 1000으로 나눠야 하므로 한번 나눠준다.
var result = divide(arr, B)
for i in 0..<result.count {
for j in 0..<result[i].count {
result[i][j] %= 1000
}
}
// 출력
for i in 0..<result.count {
print(result[i].map{String($0)}.joined(separator: " "))
}
'코딩테스트 > 백준' 카테고리의 다른 글
BOJ-9935 문자열 폭발 Swift (0) | 2024.05.07 |
---|---|
BOJ-11444 피보나치 수 6 Swift (0) | 2024.05.05 |
BOJ-11401 이항 계수 3 Swift (0) | 2024.05.03 |
BOJ-1629 곱셈 Swift (0) | 2024.05.03 |
BOJ-1780 종이의 개수 Swift (0) | 2024.05.03 |