※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※
이번에 볼 문제는 백준 15826번 문제인 Namje Adventure이다.
문제는 아래 링크를 확인하자.
https://www.acmicpc.net/problem/15826
15826번: Namje Adventure
탐험을 떠나는 사람 N, 동굴의 깊이 D, 로프의 길이 L이 주어진다. 두 번째 라인에 내려가기 위해 드는 에너지 xi를 L번에 걸쳐 입력 받는다.
www.acmicpc.net
탐험을 떠나는 사람들 사이의 거리의 최댓값은 멀어봐야 L-1이 됨을 관찰하자. 이는 항상 맨 위에 있는 사람이 다음 차례로 움직인다는 조건에 의해 자명하다.
인덱스 idx를 0부터 시작해 idx에 사람이 있으면 아래로 옮기고 없으면 다음으로 넘어가는 시행을 반복하는 방식으로 문제를 푼다고 상상해보자. 이 때, 위의 관찰에 의해 매 idx에 사람이 있는지 확인하는 경우 모든 사람이 [idx,idx+L-1] 위에 놓여있음을 알 수 있다. 즉, 각 idx에 대하여 사람들이 있는 위치상태의 경우의 수는 L개의 위치에서 N명의 사람을 (순서없이) 뽑는 것과 같음을 알 수 있다. 이 각각의 상태를 X, Y, ...와 같이 나타내겠다.
상태 X의 첫 위치에 사람이 있다면 그 사람을 (문제의 규칙에 맞게) 이동가능한 아래의 위치로 에너지를 소비하며 이동하는 것을 시도해 다음 시점(idx+1일 때의 시행)에서 관찰되는 상태 Y에 도달하기 위한 에너지 소비량의 최솟값의 후보를 계산해나가자. 상태 X의 첫 위치에 사람이 없다면 이를 다음 시점(idx+1일 때의 시행)에서의 다른 상태 Y로 에너지 0을 소비해 이동하는 것과 같이 생각해 계산해나가자.
D의 값이 작다면 위와 같은 시뮬레이션을 돌려 문제를 해결할 수 있겠으나, D의 값이 크므로 위의 계산을 빠르게 할 필요가 있다. 위의 상황을 행렬의 꼴로 나타낸 뒤 exponentiation by squaring을 이용하는 것으로 복잡도를 줄여주자.
위에서 언급한 "상태"는 그대로 변수로 사용하기에는 형태가 좋지 않으므로, 이를 적절한 정수에 잘 대응시켜 문제를 해결해주자.
아래는 제출한 소스코드이다.
#include <iostream>
#include <map>
#include <utility>
using namespace std;
typedef long long ll;
ll N, D, L;
ll mat[221][221];
ll arr[221][221];
ll ans[221];
ll tmp[221];
ll X[13];
int id;
int valtoid[1714];
int idtoval[221][3];
void solve1() {
for (int i = 0; i < L; i++) {
idtoval[id][0] = i;
valtoid[i] = id++;
}
for (int r = 0; r < id; r++) {
for (int c = 0; c < id; c++) {
mat[r][c] = 1000000000000000007LL;
}
}
for (int i = 1; i < id; i++) ans[i] = 1000000000000000007LL;
for (int cur = 0; cur < id; cur++) {
int x = idtoval[cur][0];
if (x) {
int xx = x - 1;
int nxt = valtoid[xx];
mat[cur][nxt] = 0;
}
else {
for (int i = 1; i <= L; i++) {
int xx = i - 1;
int nxt = valtoid[xx];
mat[cur][nxt] = X[i];
}
}
}
D -= N;
while (D) {
if (D & 1) {
for (int i = 0; i < id; i++) tmp[i] = 1000000000000000007LL;
for (int r = 0; r < id; r++) {
for (int c = 0; c < id; c++) {
tmp[c] = min(tmp[c], ans[r] + mat[r][c]);
}
}
swap(ans, tmp);
}
for (int r = 0; r < id; r++) {
for (int c = 0; c < id; c++) {
arr[r][c] = 1000000000000000007LL;
for (int k = 0; k < id; k++) {
arr[r][c] = min(arr[r][c], mat[r][k] + mat[k][c]);
}
}
}
swap(mat, arr);
D >>= 1;
}
cout << ans[0];
}
void solve2() {
for (int i = 0; i < L; i++) {
for (int j = i + 1; j < L; j++) {
idtoval[id][0] = i, idtoval[id][1] = j;
valtoid[i + j * L] = id++;
}
}
for (int r = 0; r < id; r++) {
for (int c = 0; c < id; c++) {
mat[r][c] = 1000000000000000007LL;
}
}
for (int i = 1; i < id; i++) ans[i] = 1000000000000000007LL;
for (int cur = 0; cur < id; cur++) {
int x = idtoval[cur][0], y = idtoval[cur][1];
if (x) {
int xx = x - 1, yy = y - 1;
int nxt = valtoid[xx + yy * L];
mat[cur][nxt] = 0;
}
else {
for (int i = 1; i <= L; i++) {
if (i < y) {
int xx = i - 1, yy = y - 1;
int nxt = valtoid[xx + yy * L];
mat[cur][nxt] = X[i];
}
else if (y < i) {
int xx = y - 1, yy = i - 1;
int nxt = valtoid[xx + yy * L];
mat[cur][nxt] = X[i];
}
}
}
}
D -= N;
while (D) {
if (D & 1) {
for (int i = 0; i < id; i++) tmp[i] = 1000000000000000007LL;
for (int r = 0; r < id; r++) {
for (int c = 0; c < id; c++) {
tmp[c] = min(tmp[c], ans[r] + mat[r][c]);
}
}
swap(ans, tmp);
}
for (int r = 0; r < id; r++) {
for (int c = 0; c < id; c++) {
arr[r][c] = 1000000000000000007LL;
for (int k = 0; k < id; k++) {
arr[r][c] = min(arr[r][c], mat[r][k] + mat[k][c]);
}
}
}
swap(mat, arr);
D >>= 1;
}
cout << ans[0];
}
void solve3() {
for (int i = 0; i < L; i++) {
for (int j = i + 1; j < L; j++) {
for (int k = j + 1; k < L; k++) {
idtoval[id][0] = i, idtoval[id][1] = j, idtoval[id][2] = k;
valtoid[i + j * L + k * L * L] = id++;
}
}
}
for (int r = 0; r < id; r++) {
for (int c = 0; c < id; c++) {
mat[r][c] = 1000000000000000007LL;
}
}
for (int i = 1; i < id; i++) ans[i] = 1000000000000000007LL;
for (int cur = 0; cur < id; cur++) {
int x = idtoval[cur][0], y = idtoval[cur][1], z = idtoval[cur][2];
if (x) {
int xx = x - 1, yy = y - 1, zz = z - 1;
int nxt = valtoid[xx + yy * L + zz * L * L];
mat[cur][nxt] = 0;
}
else {
for (int i = 1; i <= L; i++) {
if (i < y) {
int xx = i - 1, yy = y - 1, zz = z - 1;
int nxt = valtoid[xx + yy * L + zz * L * L];
mat[cur][nxt] = X[i];
}
else if (y < i && i < z) {
int xx = y - 1, yy = i - 1, zz = z - 1;
int nxt = valtoid[xx + yy * L + zz * L * L];
mat[cur][nxt] = X[i];
}
else if (z < i) {
int xx = y - 1, yy = z - 1, zz = i - 1;
int nxt = valtoid[xx + yy * L + zz * L * L];
mat[cur][nxt] = X[i];
}
}
}
}
D -= N;
while (D) {
if (D & 1) {
for (int i = 0; i < id; i++) tmp[i] = 1000000000000000007LL;
for (int r = 0; r < id; r++) {
for (int c = 0; c < id; c++) {
tmp[c] = min(tmp[c], ans[r] + mat[r][c]);
}
}
swap(ans, tmp);
}
for (int r = 0; r < id; r++) {
for (int c = 0; c < id; c++) {
arr[r][c] = 1000000000000000007LL;
for (int k = 0; k < id; k++) {
arr[r][c] = min(arr[r][c], mat[r][k] + mat[k][c]);
}
}
}
swap(mat, arr);
D >>= 1;
}
cout << ans[0];
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> N >> D >> L;
for (int l = 1; l <= L; l++) cin >> X[l];
if (N == 1) solve1();
else if (N == 2) solve2();
else solve3();
}
'BOJ' 카테고리의 다른 글
[BOJ 15825 // C++] System Call (0) | 2023.08.20 |
---|---|
[BOJ 4117 // C++] Combination Lock (0) | 2023.08.19 |
[BOJ 15831 // C++] 준표의 조약돌 (0) | 2023.08.18 |
[BOJ 3234 // C++] LUKA (0) | 2023.08.17 |
[BOJ 6904 // C++] Picture Perfect (0) | 2023.08.17 |