#12100. 2048(Easy)
Problem
- N x N 크기의 보드판
- 한 번의 이동 : 보드판에 있는 모든 블록 상하좌우 중 한 방향으로 쭉 이동
단순히 한 칸이 아닌 해당 방향 이동할 수 있는 곳 끝까지 - 같은 값을 가진 블록이 충돌하면 하나로 합쳐짐
(한 번 합쳐지면 다시 합칠 수 없음 → 한 번 이동할 때 연속으로 합쳐질 수 없음 2개 → 1개인 경우만 존재)
블록이 합쳐지면 해당 블록의 숫자를 더한 값이 된다.
Goal: 최대 5번 이동해서 만들 수 있는 가장 큰 블록의 값 구하기
입력
N: ~20
0: 빈칸
2^i(i= 1, 2…10): 블록 // 적어도 하나 주어짐
Solution
- 최대 5번 이동이니 한 번 이동하면서 최대 블록 값을 갱신해주어야 한다.
(꼭 안그래도 되겠지만)
- 5번 이동하는 조합을 구한다.
- 그 이동에 맞게 블록을 이동시킨다.
- 해당 번째의 방향을 저장할 배열을 이용해 5개의 방향을 저장해놓고, 모두 저장했을 때 보드판을 움직이도록 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17void dfs(int idx) {
if (idx == 5) { // 방향 5개가 저장되었을 때
for (int i = 0; i < n; ++i) { // 입력값인 temp를 board로 옮기고 작업을 수행한다.
for (int j = 0; j < n; ++j) {
board[i][j] = temp[i][j];
}
}
for (int i = 0; i < 5; ++i) {
moveBlock(해당 방향);
}
return;
}
for (int i = 0; i < 4; ++i) {
direction[idx] = i;
dfs(idx+1);
}
} moveBlock()
블럭 이동
움직이는 방향이 좌우면 한 행의 열이 바뀌면서 board위의 블럭들이 바뀐다.
이때 좌로 이동하면 오른쪽부터 탐색, 우로 이동하면 왼쪽부터 탐색해야 한다.
움직이는 방향이 상하일 때도 위와 같은 방식이다. 이를 잘 구분해주어 update할 때 행, 열의 인덱스를 반영한다.
update()
아래 코드는 좌우로 이동할 때 블럭들을 갱신하는 함수다.3가지 경우를 확인하고 각 작업을 수행한다.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25void updateCol(int i, int j, queue<int> &zero) {
if (board[i][j] == 0) { // 0의 위치
zero.push(j);
}
else if (board[i][j] != 0 && board[i][j] == value) { // 블럭 값이 같을 때
value = 0; // 한 번에 한 번만 합치도록 0으로 초기화
board[i][value_idx] *= 2;
answer = max(answer, board[i][value_idx]);
board[i][j] = 0;
zero.push(j);
}
else if (board[i][j] != 0) {
value_idx = j;
value = board[i][j];
answer = max(answer, value);
if (!zero.empty()) {
zero_idx = zero.front();
zero.pop();
board[i][j] = 0;
board[i][zero_idx] = value;
zero.push(j);
value_idx = zero_idx; // 변경
}
}
}
- 블럭이 없는 빈 곳: queue에 해당 위치를 넣어준다. (블럭을 옮길 때 쓰임)
- 합칠 대상이 되는 블럭: 이전에 저장해놓은 블럭 값과 같으면 합칠 수 있다.
이전에 저장해놓은 블럭의 값을 2배로 하고 합칠 대상이 되는 블럭은 0으로 바꾼뒤 이 위치를 queue에 넣어준다. - 합쳐질 가능성이 있는 블럭: 이 값은 나중에 합쳐질 수 있으므로 해당 값과 위치를 저장해 놓는다.
단, queue가 비어있지 않다면 이 블럭의 위치를 옮겨주어야 하기에 queue에서 pop한 0의 위치로 해당 블럭 값을 넣어준다.
이렇게 되면 바뀌기 전 블럭은 0이 되고 이 위치를 다시 queue에 넣어주어야 한다.
마지막으로 위치가 바뀐 지점을 갱신해주면 된다.
1 Try
1 |
|
위의 코드가 틀린 Test case
7
2 2 2 2 2 2 2
2 0 2 2 2 2 2
2 0 2 2 2 2 2
2 0 2 2 2 2 2
2 2 2 0 2 2 2
2 2 2 2 2 2 0
2 2 2 2 2 2 0
-> 32 // output
디버깅용 코드를 보면 알겠지만 방향을 3(오른쪽)으로 했을 때 board의 블럭들을 확인해보니
실제로 잘 합쳐지지 않았던 것이다. (solution에는 정답 코드를 반영)
- 위 코드의 빨간 줄을 없애주었다. 합쳐지면 0이 있는 곳을 찾아 넣는 작업을 하면 안되기 때문이다. 이 블럭은 합쳐졌고, 이 위치는 0으로 채우고 이 인덱스를 queue에 넣어주기만 하면 된다.
- queue가 비어있지 않다면 해당 지점에 블럭을 놓는 작업이 있는데
이때, value_idx = zero_idx 를 추가해주어야 해당 블럭의 바뀐 위치도 알 수 있다.
이를 추가하지 않으면 바뀌기 전 위치가 남아있어 합쳐지지 않는다.
Debug
- 코드 보기
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
using namespace std;
int n, value, value_idx, zero_idx, answer = 2;
int board[MAX][MAX];
int temp[MAX][MAX];
int direction[5]; // 방향 조합 0:상, 1:하, 2:좌, 3:우
void updateCol(int i, int j, queue<int> &zero) {
if (board[i][j] == 0) { // 0의 위치
zero.push(j);
}
else if (board[i][j] != 0 && board[i][j] == value) { // 블럭 값이 같을 때
value = 0; // 한 번에 한 번만 합치도록 0으로 초기화
board[i][value_idx] *= 2;
answer = max(answer, board[i][value_idx]);
board[i][j] = 0;
zero.push(j);
}
else if (board[i][j] != 0) {
value_idx = j;
value = board[i][j];
answer = max(answer, value);
if (!zero.empty()) {
zero_idx = zero.front();
zero.pop();
board[i][j] = 0;
board[i][zero_idx] = value;
zero.push(j);
value_idx = zero_idx; // 변경
}
}
//printf("%d-%d번재: \n", i, j);
//for (int i = 0; i < n; ++i) {
// for (int j = 0; j < n; ++j) {
// printf("%d ", board[i][j]);
// }
// printf("\n");
//}
}
void updateRow(int i, int j, queue<int> &zero) {
if (board[j][i] == 0) { // 0의 위치
zero.push(j);
}
else if (board[j][i] != 0 && board[j][i] == value) { // 블럭 값이 같을 때
value = 0; // 한 번에 한 번만 합치도록 0으로 초기화
board[value_idx][i] *= 2;
answer = max(answer, board[value_idx][i]);
board[j][i] = 0;
zero.push(j);
}
else if (board[j][i] != 0) {
value_idx = j;
value = board[j][i];
answer = max(answer, value);
if (!zero.empty()) {
zero_idx = zero.front();
zero.pop();
board[j][i] = 0;
board[zero_idx][i] = value;
zero.push(j);
value_idx = zero_idx; // 변경
}
}
}
void moveBlock(int move) {
for (int i = 0; i < n; ++i) {
queue<int> zero;
value = 0; value_idx = 0; zero_idx = 0;
if (move % 2 == 0) {
for (int j = 0; j < n; ++j) {
if (move == 0) {
updateRow(i, j, zero);
}
else {
updateCol(i, j, zero);
}
}
}
else {
for (int j = n-1; j >= 0; --j) {
if (move == 1) {
updateRow(i, j, zero);
}
else {
updateCol(i, j, zero);
}
}
}
}
}
void dfs(int idx) {
if (idx == 5) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
board[i][j] = temp[i][j];
}
}
for (int i = 0; i < 5; ++i) {
moveBlock(direction[i]);
}
return;
}
for (int i = 0; i < 4; ++i) {
direction[idx] = i;
dfs(idx+1);
}
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
scanf("%d", &temp[i][j]);
}
}
//dfs(0);
direction[0] = 3;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
board[i][j] = temp[i][j];
}
}
moveBlock(direction[0])
printf("\n");
printf("%d\n", answer);
return 0;
}2 Try
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
using namespace std;
int n, value, value_idx, zero_idx, answer = 2;
int board[MAX][MAX];
int temp[MAX][MAX];
int direction[5]; // 방향 조합 0:상, 1:하, 2:좌, 3:우
void updateCol(int i, int j, queue<int> &zero) {
if (board[i][j] == 0) { // 0의 위치
zero.push(j);
}
else if (board[i][j] != 0 && board[i][j] == value) { // 블럭 값이 같을 때
value = 0; // 한 번에 한 번만 합치도록 0으로 초기화
board[i][value_idx] *= 2;
answer = max(answer, board[i][value_idx]);
board[i][j] = 0;
zero.push(j);
}
else if (board[i][j] != 0) {
value_idx = j;
value = board[i][j];
answer = max(answer, value);
if (!zero.empty()) {
zero_idx = zero.front();
zero.pop();
board[i][j] = 0;
board[i][zero_idx] = value;
zero.push(j);
value_idx = zero_idx; // 변경
}
}
}
void updateRow(int i, int j, queue<int> &zero) {
if (board[j][i] == 0) { // 0의 위치
zero.push(j);
}
else if (board[j][i] != 0 && board[j][i] == value) { // 블럭 값이 같을 때
value = 0; // 한 번에 한 번만 합치도록 0으로 초기화
board[value_idx][i] *= 2;
answer = max(answer, board[value_idx][i]);
board[j][i] = 0;
zero.push(j);
}
else if (board[j][i] != 0) {
value_idx = j;
value = board[j][i];
answer = max(answer, value);
if (!zero.empty()) {
zero_idx = zero.front();
zero.pop();
board[j][i] = 0;
board[zero_idx][i] = value;
zero.push(j);
value_idx = zero_idx; // 변경
}
}
}
void moveBlock(int move) {
for (int i = 0; i < n; ++i) {
queue<int> zero;
value = 0; value_idx = 0; zero_idx = 0;
if (move % 2 == 0) {
for (int j = 0; j < n; ++j) {
if (move == 0) {
updateRow(i, j, zero);
}
else {
updateCol(i, j, zero);
}
}
}
else {
for (int j = n-1; j >= 0; --j) {
if (move == 1) {
updateRow(i, j, zero);
}
else {
updateCol(i, j, zero);
}
}
}
}
}
void dfs(int idx) {
if (idx == 5) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
board[i][j] = temp[i][j];
}
}
for (int i = 0; i < 5; ++i) {
moveBlock(direction[i]);
}
return;
}
for (int i = 0; i < 4; ++i) {
direction[idx] = i;
dfs(idx+1);
}
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
scanf("%d", &temp[i][j]);
}
}
dfs(0);
printf("%d\n", answer);
return 0;
}