Seren dev's blog
article thumbnail
 

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
profile

Seren dev's blog

@Seren dev

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