클래스 문제에 있길래 풀어보았다.
문제는 아래와 같다.
https://www.acmicpc.net/problem/17401
17401번: 일하는 세포
출력은 N개의 줄로 구성되며, i 번째 줄에는 N개의 정수 xi1, xi2, ..., xiN를 공백으로 구분하여 출력해야 한다. xij는 0초 때 거점 i 에서 출발하여 정확히 D초 때 거점 j에 위치하게 되는 경로의 수를 1
www.acmicpc.net
의식의 흐름 및 해설
우선 D가 굉장히 크다.
어지간한 그래프 탐색 (dfs, bfs, floyd)로는 시간복잡도 내에 절대 불가능해보인다.
그런데, 이렇게 이동 경우의 수를 구하는 문제, 어디서 많이 보지 않았나?
바로 본대산책2에서 본 듯하다.
https://www.acmicpc.net/problem/12850
12850번: 본대 산책2
가능한 경로의 수를 1,000,000,007로 나눈 나머지를 출력한다.
www.acmicpc.net
위와 같이 경우의수를 구할 땐, 인접행렬과 그래프 성질을 이용해주면 된다.
여기서는 주기에 따라 다른 행렬을 곱하는데, 여기서 주의해주어야한다.
예제2처럼 T가 3이라 해보자.
두 가지 방법이 있는데 어떤 것으로 해결하겠는가?
어차피 A^3 * B^3 * C^2 이나, (ABC)^2*AB나 똑같으니까
A=power(A, 3) * B=power(B, 3) * C=power(C, 2) 하면 되지 않을까~? 할 수도 있다.
그러나 제일 중요한 점은, 행렬은 교환법칙이 성립하지 않는다.
자세한 이유는 아래 블로그의 포스팅을 참고하자.
행렬의 곱셈에 대한 성질
행렬의 곱셈 방법에 대해 알아봤으니 이제 행렬의 곱셈에 대한 성질을 알아볼 차례에요. 덧셈, 곱셈에 대한 성질은 자리를 바꿔도 되는 교환법칙, 연산 순서를 바꿔도 되는 결합법칙, 괄호를 풀
mathbang.net
따라서 2번 방법인 (ABC)^2*AB로 문제에서 주어진 순서를 지켜서 해주어야한다.
코드
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <cstring>
#include <queue>
#include <cmath>
#include <stack>
#include <set>
#include <map>
#define all(v) (v).begin(), (v).end()
#define press(v) (v).erase(unique(all(v)), (v).end())
using namespace std;
typedef long long ll;
typedef pair<int, int> pi;
typedef pair<int, pi> pii;
const int MAX = 101;
const int INF = 0x3f3f3f3f;
const ll LNF = 1e18;
const ll MOD = 1e9 + 7;
int T, N, D;
class Matrix {
public:
vector<vector<ll>> v;
Matrix() {
v = vector<vector<ll>>(N, vector<ll>(N, 0));
}
Matrix operator*(const Matrix O) const {
Matrix R;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < N; k++) {
R.v[i][j] += v[i][k] * O.v[k][j];
R.v[i][j] %= MOD;
}
}
}
return R;
}
};
Matrix power(Matrix M, ll k) {
Matrix R;
if (!k) {
for (int i = 0; i < N; i++) {
R.v[i][i] = 1;
}
return R;
}
if (k == 1)return M;
R = power(M, k / 2);
R = R * R;
if (k % 2)
R = R * M;
return R;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> T >> N >> D;
Matrix M[MAX];
for (int i = 0; i < N; i++) {
M[0].v[i][i] = 1;
}
for (int i = 1; i <= T; i++) {
int t;
cin >> t;
while (t--) {
ll a, b, cost;
cin >> a >> b >> cost;
a--; b--;
M[i].v[a][b] = cost;
}
}
for (int i = 1; i <= T; i++) {
M[0] = M[0] * M[i];
}
M[0] = power(M[0], 1LL*D / T);
for (int i = 1; i <= D % T; i++)
M[0] = M[0] * M[i];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
cout << M[0].v[i][j] << " ";
}
cout << "\n";
}
}
행렬의 중요성...
선형대수학의 중요성...
'PS > BOJ' 카테고리의 다른 글
[BOJ] 백준 1867. 돌멩이 제거 (Platinum III) (0) | 2021.10.17 |
---|---|
[BOJ] 백준 1990. 소수인팰린드롬 (Gold V) (0) | 2021.10.17 |
[BOJ] 백준 22345. 누적 거리 (Gold III) (0) | 2021.10.17 |
[BOJ] 백준 23237. How Many Subtrees? (Gold IV) (0) | 2021.10.14 |
[BOJ] 백준 14168. Cow Checklist (Gold I) (0) | 2021.10.14 |