클릭시 문제로 이동합니다.
어떻게 풀까?
해당 문제는 크게 2 부분으로 나눌 수 있습니다.
1. 재귀를 이용하여 블록을 위/아래/왼쪽/오른쪽 으로 최대 5번 이동시키는 부분
2. 이동시켜서 최댓값이 어떻게 되는지 알아내는 부분
이 중, 1번은 2차원 배열 restore[][]를 이용하면, 기존의 맵을 저장하고 복구하면서 맵을 5번까지 이동시키는 방법으로 구현할 수 있습니다.
가장 중요한건 2번이죠!
switch를 쓰면 비슷한 방법을 4번 반복해야하기 때문에, 짧게 이를 해결하는 방법을 소개해드리겠습니다.
(물론, 실전에서 이 방법까지 생각하려면 힘들겠지만, 그래도! 여긴 블로그니까요!)
우선, 기본적으로 블록을 이동시키는 방법은 덱을 이용하는 것입니다.
덱의 맨 뒤의 수와, 현재 맵에 적혀있는 블록들을 확인하면서, 두 수가 같으면 더해주는 것이죠!
또한, 더해진 수에 또 한번 더해지면 안되기 때문에 canplus라는 변수를 사용합시다!
canplus가 true면, 현재 덱의 맨 뒤의 수가 더해질 수 있다는 것을 의미하고, false인 경우에는 더해질 수 없다는 것을 의미합니다!
예를 들어, 1, 1, 2, 4블럭을 왼쪽으로 이동시켜 보겠습니다.
1을 큐에 넣습니다! 다음에 1이 들어오면 두 수가 더해질 수 있기 때문에 canplus는 true 입니다.
그리고, 초록색 부분을 0으로 채웁니다.
canplus가 true이고 덱의 맨 뒤가 1이기 때문에 두 수를 합칩니다.
그리고, 합쳐졌으니 canplus는 false로 만들고, 초록색 부분은 0으로 만듭니다.
같은 2가 들어왔지만, 합쳐지지 않습니다.
초록색 부분을 0으로 만듭니다.
canplus가 false이므로 덱의 맨 뒤에는 4를 넣습니다.
그리고 이제 맵의 끝에 닿았기 때문에, 덱의 앞에서부터 다시 맵을 채워넣으면,
이렇게 완성됩니다!
하지만, 왼쪽, 오른쪽, 위, 아래로 블럭이 이동할때마다 기준점이 달라집니다. 어떻게 하면 이 기준점을 쉽게 만들 수 있을까요!?
자! 우선 규칙을 정합시다.
d는 블럭들이 이동해야하는 방향을 의미합니다.
d[0] = 왼쪽
d[1] = 오른쪽
d[2] = 위
d[3] = 아래
자, 블럭들이 왼쪽으로 이동해야 하는 경우입니다.
이 경우에, 블록을 이동시키기 위해서는 어떤 행을 기준으로 가장 왼쪽부터 이동해야합니다.
아래 그림에서 초록색으로 칠한 부분들과 같죠.
이와 유사하게, 오른쪽, 위, 아래로 이동해야하는 경우는 아래 그림들과 같습니다.
즉, 4 개의 처음 확인해야하는 지점이 모두 다른 것을 알 수 있습니다.
하지만, 왼쪽과 오른쪽, 위와 아래로 묶으면 약간의 공통점이 있습니다.
왼쪽과 오른쪽은 0행부터 n행까지 왼쪽에서 오른쪽, 오른쪽에서 왼쪽으로 본다는 것입니다. 즉, 왼쪽으로 이동할때는 다음의 맵이 arr[0][1]로 1 증가하고, 오른쪽으로 이동해야할 때는 덱에 넣어야할 다음 맵의 위치가 arr[0][n-2]가 됩니다.
시작점 : arr[0][0], arr[0][n-1]
다음 지점 : 열 +1, 열 -1
위와 아래는 이와 반대입니다.
시작점 : arr[0][0], arr[n-1][0]
다음 지점 : 행 +1, 행 -1
이를 이용해서 다음 지점으로 이동해야하는 dm[4] 를 {1,-1,1,-1}로 하고,
시작지점을 start[4] = {0,1,0,1}로 해서
처음 시작지점을 a, b 변수로 두고,
a를 0, b를 start[d] * (n-1)로 한 뒤에,
상하를 보는지, 위 아래를 보는지에 따라서 arr[a][b]를 할지 arr[b][a]를 볼지 정해주면 됩니다.
그리고, b값에 dm[d]를 더해주면 되죠!
만약, b가 0이 되거나 n이 된다면, a를 1 늘려서 위의 과정을 다시 반복하면 됩니다.
마지막으로, 맵을 훑어 보면서 최댓값이 무엇인지 찾으면 되죠!
코드
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 | //https://www.acmicpc.net/problem/12100 // BOJ 12100 2048(easy) #pragma once #include<iostream> #include<cstring> using namespace std; int map[20][20], n; int dm[4] = { 1,-1,1,-1 }; int start[4] = { 0,1,0,1 }; int MAX(int i1, int i2) { return i1 < i2 ? i2 : i1; } void move(int d) { int que[20], s = 0, e = 0; for (int j = 0; j < n; j++) { s = e = 0; bool canpl = false; int r = start[d] * (n - 1); while (r >= 0 && r < n) { int &nblock = d & 2 ? map[j][r] : map[r][j]; if (nblock) { if (canpl && que[e - 1] == nblock) { canpl = false; que[e - 1] <<= 1; nblock = 0; } else { canpl = true; que[e++] = nblock; nblock = 0; } } r += dm[d]; } r = start[d] * (n - 1); while (s != e) { int &nblock = d & 2 ? map[j][r] : map[r][j]; nblock = que[s++]; r += dm[d]; } } } int dfs(int depth, int dir) { if (depth == 5) { int max = 0; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) max = MAX(map[i][j], max); return max; } int restore[20][20]; memcpy(restore, map, sizeof(map)); int num = 0; for (int i = 0; i < 4; i++) { move(i); num = MAX(num, dfs(depth + 1, i)); memcpy(map, restore, sizeof(map)); } return num; } int main() { cin >> n; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { cin >> map[i][j]; } } cout << dfs(0, 0); return 0; } | cs |
시간 복잡도
총 시간복잡도는 입니다.
물론, 재귀함수가 4개의 노드씩 5번 생성되기 때문에 번 돌아갑니다!
'공부 > 알고리즘 문제풀이' 카테고리의 다른 글
[삼성 기출 문제] 백준 14499 주사위 굴리기 (1) | 2018.12.01 |
---|---|
[삼성 기출 문제] 백준 3190 뱀 (0) | 2018.11.30 |
[삼성 기출 문제] 백준 13460 구슬 탈출 2 (3) | 2018.11.08 |
[BOJ] 5623. 수열의 합 (0) | 2018.09.18 |
[BOJ] 2789. 유학 금지 (0) | 2018.09.18 |