Seren dev's blog
article thumbnail
 

10830번: 행렬 제곱

크기가 N*N인 행렬 A가 주어진다. 이때, A의 B제곱을 구하는 프로그램을 작성하시오. 수가 매우 커질 수 있으니, A^B의 각 원소를 1,000으로 나눈 나머지를 출력한다.

www.acmicpc.net

1. 풀이

재귀함수를 이용한 분할정복으로 문제를 풀 수 있다.

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) : 분할정복을 사용하는 재귀함수

<java />
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)을 바로 리턴한다.

 

2. 코드

<java />
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; } }

 

3. 수정한 버전

효율적인 캐싱을 위한 행렬 곱셈 방식

<java />
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; }

 

전체 코드

<java />
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; } }

 

4. Reference

728x90
profile

Seren dev's blog

@Seren dev

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!