10830번: 행렬 제곱
크기가 N*N인 행렬 A가 주어진다. 이때, A의 B제곱을 구하는 프로그램을 작성하시오. 수가 매우 커질 수 있으니, A^B의 각 원소를 1,000으로 나눈 나머지를 출력한다.
www.acmicpc.net
풀이
재귀함수를 이용한 분할정복으로 문제를 풀 수 있다.
n, b, arr 배열 값을 입력받은 다음 divideAndConquer() 재귀함수를 호출하여 arr의 b제곱(result 배열)을 구한다.
주의할 점
- B의 범위는 1 ≤ B ≤ 100,000,000,000 이기 때문에 int로 값을 받으면 NumberFormatException이 발생한다. 그래서 Long.parseLong을 사용하여 long으로 값을 받아야 한다.
- A^B의 각 원소를 1,000으로 나눈 나머지를 출력해야 하므로, 행렬 곱셈을 할 때마다 결과 배열의 원소 값을 1000으로 나누어주어야 하고, 이후 최종적으로 배열을 출력할 때도 배열의 원소 값을 1000으로 나눈 값을 출력해야 한다.
int[][] divideAndConquer(int[][] arr, long b) : 분할정복을 사용하는 재귀함수
static int[][] divideAndConquer(int[][] arr, long b) {
if(b == 1) {
return arr;
}
int[][] result = divideAndConquer(arr, b/2);
result = multipleArray(result, result);
if (b % 2 == 1) {
result = multipleArray(result, arr);
}
return result;
}
- b가 1이면 2차원 배열(arr)을 바로 리턴한다.
코드
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
long b = Long.parseLong(st.nextToken());
int[][] arr = new int[n][n];
for (int i = 0; i < n; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < n; j++) {
arr[i][j] = Integer.parseInt(st.nextToken());
}
}
int[][] result = divideAndConquer(arr, b);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++)
System.out.print(result[i][j] % 1000 + " ");
System.out.println();
}
}
static int[][] divideAndConquer(int[][] arr, long b) {
if(b == 1) {
return arr;
}
int[][] result = divideAndConquer(arr, b/2);
result = multipleArray(result, result);
if (b % 2 == 1) {
result = multipleArray(result, arr);
}
return result;
}
static int[][] multipleArray(int[][] arr1, int[][] arr2) {
int n = arr1.length;
int[][] result = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
int sum = 0;
for (int k = 0; k < n; k++)
sum += arr1[i][k] * arr2[k][j];
result[i][j] = sum % 1000;
}
}
return result;
}
}
수정한 버전
효율적인 캐싱을 위한 행렬 곱셈 방식
static int[][] multipleArray(int[][] arr1, int[][] arr2) {
int n = arr1.length;
int[][] result = new int[n][n];
for (int i = 0; i < n; i++) {
for (int k = 0; k < n; k++) {
int r = arr1[i][k];
// arr1(ik) 원소를 고정시켜두고, 그에 대한 arr2의 k열을 고정시켜 j행을 움직이면서 연산한다.
for (int j = 0; j < n; j++) {
result[i][j] += r * arr2[k][j];
result[i][j] %= 1000;
}
}
}
return result;
}
전체 코드
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
long b = Long.parseLong(st.nextToken());
int[][] arr = new int[n][n];
for (int i = 0; i < n; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < n; j++) {
arr[i][j] = Integer.parseInt(st.nextToken());
}
}
int[][] result = divideAndConquer(arr, b);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++)
System.out.print(result[i][j] % 1000 + " ");
System.out.println();
}
}
static int[][] divideAndConquer(int[][] arr, long b) {
if (b == 1) {
return arr;
}
int[][] result = divideAndConquer(arr, b / 2);
result = multipleArray(result, result);
if (b % 2 == 1) {
result = multipleArray(result, arr);
}
return result;
}
static int[][] multipleArray(int[][] arr1, int[][] arr2) {
int n = arr1.length;
int[][] result = new int[n][n];
for (int i = 0; i < n; i++) {
for (int k = 0; k < n; k++) {
int r = arr1[i][k];
for (int j = 0; j < n; j++) {
result[i][j] += r * arr2[k][j];
result[i][j] %= 1000;
}
}
}
return result;
}
}
Reference
728x90
'Algorithm 문제 풀이 > 백준' 카테고리의 다른 글
[백준] 1753번 : 최단경로(다익스트라) - 자바[Java] (0) | 2022.11.19 |
---|---|
[백준] 10816번 : 숫자 카드 2 - 자바[Java] (0) | 2022.11.17 |
[백준] 2630번 : 색종이 만들기 - 자바[Java] (0) | 2022.11.17 |
[백준] 9184번 : 신나는 함수 실행 - 자바[Java] (0) | 2022.10.25 |
[백준] 1932번 : 정수 삼각형 - 자바[Java] (0) | 2022.10.24 |