드래곤 커브
어떻게 풀까?
해당 문제는 규칙을 이용해서 풀었습니다.
해당 문제는 몇 번 손으로 답을 구해가면 규칙을 찾을 수 있습니다.
우선, 나오는 글자는
+FX, -FX, +YF, -YF가 반복해서 나오는 구조입니다.
또한,
+FX에서 세대가 늘어나면 +FX+YF
-FX에서 세대가 늘어나면 -FX+YF
+YF에서 세대가 늘어나면 +FX-YF
-YF에서 세대가 늘어남녀 -FX-YF가 됩니다.
이렇게 보면, 0 세대 글자는 +FX로 해도 아무런 문제가 없다는 것을 깨달을 수 있습니다!
(심지어, l이 1부터 시작하기 때문에 인덱스 문제도 쉽게 해결할 수 있습니다. 0번 인덱스가 +로 채워져 있기 때문이죠!)
처음에 입력으로 주어지는 p에서 나누기 3을 하면 몇 번 째 글자인지 알 수 있습니다.
3번 예제같은 경우 p가 6이므로, 2번째 글자를 원한다는 것을 알 수 있습니다!
그리고, p%3을 하면 글자에서 몇 번째 문자를 원하는지 알 수 있습니다.
3번 예제로 다시 설명을 드리겠습니다!
2세대: +FX+YF+FX-YF
2세대의 2 번째 글자는 +YF입니다.
p가 7이면 7%3 = 1 이므로, 2번째 글자의 1번 인덱스글자인 Y부터 출력하면 되고,
p가 8이면 8%3 = 2 이므로, 2번째 글자의 2번 인덱스글자인 F부터 출력하면 됩니다.
그렇다면 해당 세대의 글자는 어떻게 구할 수 있을까요!?
해당 세대의 글자에서 나누기 2를 한 위치에 있는 전 세대의 글자를 이용하면 바로 해당 세대의 글자를 얻을 수 있습니다!
1세대와 2세대를 예로 설명하겠습니다!
- 1세대: +FX+YF
- 2세대: +FX+YF+FX-YF
만약 50세대의 524번째 글자부터 시작하는 글자를 찾기 위해서라면, len[50]에 524를 저장합니다.
그러면 나머지 len값은 len[gen] = len[gen+1] / 2 의 식을 통해서 저장할 수 있습니다!
그러면 이제 memoi[gen][idx] 에는 idx + len[gen] 위치의 글자가 들어있게 됩니다!
l이 최대 50개 이므로, 최악의 경우를 대비해서 18글자가 들어갈 수 있도록 하면 문제가 없습니다!
출력할 때에도 memoi[gen]을 이용하여 결과 문자열을 만들어서 p%3부터 p%3 + l - 1 까지의 글자를 출력해주면 됩니다!
그리고, 만약 찾고자 하는 글자가 짝수라면, 부모 글자에서 2개의 글자를 생성할 수 있기 때문에 다음 글자까지 알아낼 수 있습니다!
이를 이용하면 조금 더 메모이제이션을 빠르게 수행할 수 있을 것입니다!
코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | #pragma once #include<cstdio> #include<cstring> char res[55]; // 결과 저장 const char type[4][4] = { "+FX", "-FX", "+YF", "-YF"}; //글자 타입은 4 개가 나온다. const int conv[4][2] = { {0,2}, {1,2}, {0,3}, {1,3} }; //부모 글자로부터 만들어질 수 있는 글자들 int memoi[51][20]; int st[51]; // 메모이제이션에서 시작 되는 글자의 위치 void getGen(int gen, int p) { int &ret = memoi[gen][p - st[gen]]; if (ret != -1) { return; } //부모 세대 글자를 알아낸다. getGen(gen - 1, p / 2); //부모 세대로부터 만들어지는 2 개의 글자들 int *change = (int*)conv[memoi[gen - 1][p / 2 - st[gen-1]]]; //하나밖에 못채움 if (p % 2) { ret = change[1]; } //두개 다 채울 수 있음 else { ret = change[0]; memoi[gen][p - st[gen] + 1] = change[1]; } } int main() { int t; scanf("%d\n", &t); for (int tc = 1; tc <= t; tc++) { memset(memoi, -1, sizeof(memoi)); memoi[0][0] = 0; // 첫 번째 세대 글자는 +FX int gen, p, l; scanf("%d %d %d\n", &gen, &p, &l); st[gen] = p / 3; //첫번째 글자의 시작 위치 //첫 번째 글자의 위치로 부터 부모 세대들의 시작 글자 위치를 알 수 있다. for (int i = gen - 1; i >= 0; i--) { st[i] = st[i+1] / 2; } //글자를 찾는다. for (int i = 0; i < l / 3 + 2; i++) { if (memoi[gen][i] == -1) getGen(gen, st[gen] + i); } int len = 0; //찾은 글자를 바탕으로 결과값을 채운다. for (int i = 0; i < l / 3 + 2; i++) { res[len++] = type[memoi[gen][i]][0]; res[len++] = type[memoi[gen][i]][1]; res[len++] = type[memoi[gen][i]][2]; } res[l + p % 3] = 0; printf("%s\n", res + p % 3); } return 0; } | cs |
여담
시간복잡도는 어떻게 될까요!?
getGen() 함수는 입력받은 세대 수(n) 만큼 진행되고, 해당 함수를 l / 3 + 2번 반복하기 때문에
O(n * l)이 됩니다.
'공부 > 알고리즘 문제풀이' 카테고리의 다른 글
[SW Expert Academy] 괄호 짝짓기 (0) | 2018.07.01 |
---|---|
[SW Expert Academy] 길찾기 (0) | 2018.07.01 |
[SW Expert Academy] 코드 배틀! (0) | 2018.06.28 |
[알고스팟] k번째 최대 증가 부분 수열 (2) | 2018.06.22 |
[알고스팟] 모스 부호 사전 (0) | 2018.06.21 |