※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※
이번에 볼 문제는 백준 16467번 문제인 병아리의 변신은 무죄이다.
문제는 아래 링크를 확인하자.
16467번: 병아리의 변신은 무죄
학교공부를 끝내고 집을 가던 다진이는 길가에서 병아리를 팔고 있는 아저씨를 발견했다. 병아리를 무척 사고 싶었던 다진이는 병아리의 상태를 확인하지도 않고 한 마리를 사서 집으로 향했다
www.acmicpc.net
이 문제는 최대 100개의 테스트케이스에 대해서, 문제에서 주어진 점화관계를 이용해 최대 1억번째 항을 계산해내는 문제이다. 따라서 만약 점화관계를 그대로 반복계산해 나간다면, 시간초과가 날 수밖에 없다.
따라서 위와 같은 시간초과를 피하기 위해
1) 점화관계를 행렬의 곱으로 표현하고
2) 행렬의 거듭제곱을 binary exponentiation을 통해 빠르게 계산하는 방법을 이용해볼 수 있다.
이 문제에서 요구하는 점화식은 다음과 같은 단순한 형태이다.
(다음 날 병아리 수) = (오늘 병아리 수) + (K일 전날 병아리 수)
이 문제에서 modulo 연산을 요구하는 숫자가 1,000,000,007이 아닌 100,000,007임에 유의하자.
아래는 제출한 소스코드이다.
#include <iostream>
using std::cin;
using std::cout;
typedef long long ll;
int main()
{
int T; cin >> T;
for (int t = 0;t < T;t++) {
int K, N;
cin >> K >> N;
ll mat[12][12];
ll ans[12];
for (int i = 0;i <= K;i++) {
ans[i] = 0;
for (int j = 0;j <= K;j++) {
mat[i][j] = 0;
}
}
for (int i = 0;i < K;i++) {
mat[i + 1][i] = 1;
}
mat[0][0] = 1;
mat[0][K] += 1; // K=0인 경우 mat[0][0]에 1을 더하는게 맞다
ans[0] = 1;
while (N > 0) {
if (N & 1) { // ans에 mat을 곱한다
ll ans_t[11];
for (int i = 0;i <= K;i++) {
ans_t[i] = 0;
for (int j = 0;j <= K;j++) {
ans_t[i] += mat[i][j] * ans[j];
}
}
for (int i = 0;i <= K;i++) {
ans[i] = ans_t[i]%100000007;
}
}
// mat의 제곱을 구한다.
ll mat_t[12][12];
for (int i = 0;i <= K;i++) {
for (int j = 0;j <= K;j++) {
mat_t[i][j] = 0;
for (int k = 0;k <= K;k++) {
mat_t[i][j] += mat[i][k] * mat[k][j];
}
}
}
for (int i = 0;i <= K;i++) {
for (int j = 0;j <= K;j++) {
mat[i][j] = mat_t[i][j]%100000007;
}
}
N >>= 1;
}
cout << ans[0] << '\n';
}
return 0;
}
728x90
'BOJ' 카테고리의 다른 글
[BOJ 14698 // C++] 전생했더니 슬라임 연구자였던 건에 대하여 (Hard) (0) | 2021.03.01 |
---|---|
[BOJ 2691 // C++] 이항 쇼다운 (0) | 2021.02.28 |
[BOJ 11404 // C++] 플로이드 (0) | 2021.02.26 |
[BOJ 14938 // C++] 서강그라운드 (0) | 2021.02.25 |
[BOJ 11779 // C++] 최소비용 구하기 2 (0) | 2021.02.24 |