10830번: 행렬 제곱
크기가 N*N인 행렬 A가 주어진다. 이때, A의 B제곱을 구하는 프로그램을 작성하시오. 수가 매우 커질 수 있으니, A^B의 각 원소를 1,000으로 나눈 나머지를 출력한다.
www.acmicpc.net
분할 정복 연습 문제다.
이 문제를 풀기 전에
2740번: 행렬 곱셈
첫째 줄에 행렬 A의 크기 N 과 M이 주어진다. 둘째 줄부터 N개의 줄에 행렬 A의 원소 M개가 순서대로 주어진다. 그 다음 줄에는 행렬 B의 크기 M과 K가 주어진다. 이어서 M개의 줄에 행렬 B의 원소 K개
www.acmicpc.net
이 문제를 먼저 풀 줄 알아야 한다.
행렬 a, b가 있다면 a의 행과 b의 열을 같은 인덱스에 각각 곱해서 합친 값이 그 값이다.
이 문제를 풀 때에는 operator * 를 사용해서 pow계산을 하기 편하게 해 줬다.
vii operator* (const vii& a, const vii& b) {
int sz = a.size();
vii ret(sz, vi(sz));
f(i, sz) {
f(j, sz) {
f(k, sz) {
ret[i][j] += a[i][k] * b[k][j];
}
ret[i][j] %= 1000;
}
}
return ret;
}
사실 ps를 하면서 이렇게 operator를 사용할 일이 없어서 재밌었다.
행렬 제곱은 행렬 곱셈을 b만큼 해주면 된다. 위 코드의 operator 함수를 구성했다면 나머지는 그냥
pow함수를 O(log N)으로 해주면 된다.
vii power(vii& a, ll b) {
int sz = a.size();
vii ret(sz, vi(sz));
f(i, sz) ret[i][i] = 1; //단위 행렬
while (b) {
if (b % 2) ret = ret * a;
b /= 2;
a = a * a;
}
return ret;
}
곱셈을 할 때에는 그냥 곱해주면 안 되고 단위행렬을 만들어 주고 거기다 곱해줘야 한다.
#include <iostream>
#include <vector>
using namespace std;
using ll = long long;
#define vii vector<vector<ll>>
#define vi vector<ll>
#define f(i,n) for (int i=0;i<n;i++)
vii operator* (const vii& a, const vii& b) {
int sz = a.size();
vii ret(sz, vi(sz));
f(i, sz) {
f(j, sz) {
f(k, sz) {
ret[i][j] += a[i][k] * b[k][j];
}
ret[i][j] %= 1000;
}
}
return ret;
}
vii power(vii& a, ll b) {
int sz = a.size();
vii ret(sz, vi(sz));
f(i, sz) ret[i][i] = 1;
while (b) {
if (b % 2) ret = ret * a;
b /= 2;
a = a * a;
}
return ret;
}
int main() {
ll n, b;
cin >> n >> b;
vii ar(n, vi(n));
f(i, n) f(j, n) cin >> ar[i][j];
vii ans = power(ar, b);
f(i, n) {
f(j, n) {
cout << ans[i][j] << ' ';
}
cout << '\n';
}
return 0;
}
분할 정복은 이해가 잘 안 되는 것 같아서 더 풀어봐야겠다.
'알고리즘 > BOJ' 카테고리의 다른 글
BOJ 1780 종이의 개수 (0) | 2020.09.18 |
---|---|
BOJ 1992 쿼드트리 (0) | 2020.09.18 |
BOJ 2343 기타 레슨 (0) | 2020.09.15 |
BOJ 2512 예산 (0) | 2020.09.14 |
백준 11401 이항 계수 3 (0) | 2020.09.13 |