본문 바로가기

Problem/DFS

[백준알고리즘] 12094번 2048(HARD)

https://www.acmicpc.net/problem/12094





나에게 똥을 준 문제.. ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 거의 정신 놓고 풀었음...


하도하도 안되서 다른 분들 것을 참고하였다...


정답 비율에 일조한 나의 채점 현황^^* 워매 힘든거..





dfs를 이용하여 완전탐색으로 특정 조건이 되었을 때 최대값을 찾는 문제이다!

2048(EASY)와는 다르게 2048(HARD)는 count가 10까지 가야하기 때문에 수행시간을 줄이는 것이 핵심이 되는 문제이다!!



<수행시간을 줄이기 위해 내가 생각했던 방법들>



첫 번째로, 모든 count 마다의 board에 대한 visited 배열을 만들어서 저장된 최댓값보다 현재가 작은 값인 경우 가지 않는 것으로 구현하려 했다.

ex) 4번 이동해서 1024가 나온 것이 저장된 최대값인데, 지금 4번 이동해서 512가 나왔으면 더 이상 하지 않아도 되기 때문!

visited는 각 count마다 저장된 방문한 보드판

visited[count][board_value];


count와 각 배열의 위치의 값마다 다른 경우라는 것을 저장하기 위해 ((각 인덱스*배열의 값)을 모두 더한 것) 의 크기만큼 board_value를 결정하고자 하였다. 

그런데 초기값이 1024에 20*20 행렬 이면 board_value의 크기가 매우매우 커져버려서 컴파일 에러, 메모리 초과 등이 나기 때문에 이 방법은 실행할 수 없는 방법이었다.. 




두 번째로, 상하좌우로 이동하였을 때, 이전의 board와 완전히 일치한다면 count를 하나 늘려서 같은 board의 경우로 갈 필요가 없기 때문에 이러한 경우를 제외시키는 것을 생각하였다.


그러나 이 방법만 사용했을 때에는 여전히 시간 초과가 났다 ㅠㅠ!




세 번째로, 첫 번째 방법을 변경하여 board 자체에 대한 visited를 만들지 않고 최대값에 대한 visited를 만들었다.

ex) ans는 각 count마다 저장된 최대값

ans[count]

계속 현재 count에서의 board의 최대값을 찾아 ans[count]에 저장된 값과 비교하여 진행 여부를 찾는 방식이었다.



그러나 이 방법 역시 문제를 완전히 해결해 주지 못했다.





계속 해도 도저히 이제 모르겠어서 ㅠㅠ 다른 사람들의 코드를 참고하였다.


시간을 줄이는 방법의 핵심 조건은 두 가지였다.

1) 상하좌우 이동시 board가 변경되지 않으면 그 방향은 진행하지 않는다.

2) 현재 최대값에서 10번 움직였을 때의 최대값에 도달할 수 없는 경우 되돌아간다.



내가 생각한 방법과 유사하다고 생각하며 위 방법들을 참고하여 다시 코드를 짜봐도 계속 시간초과가 났다 ㅠㅠㅠㅠ



그러다가 결국 내가 제대로 캐치하지 못했던 것 몇 가지를 알 수 있었다!!!!!


1) N = 1일 때, 나의 코드에서는 보드가 갱신될 수 없었으므로 count = 10까지 가지 않는다. 이에 대한 예외처리가 필요하다.

2) 최대값에 도달할 수 없는 경우를 찾기 위해, N =10이 아닐 때, 현재 최대값이 가져야 하는 값을 / 를 하며 찾아나갔는데 나눗셈 연산을 지속적으로 하는 게 시간 초과의 원인이 되었다.  따라서 N = 10일 때, 나눈 값을 저장해 놓고 사용함으로써 해결할 수 있었다. 



시간 초과 문제 해결하느라 정말 힘들었다..ㅠㅠ 이 문제는 나중에 꼭 다시 재점검하면서 핵심적인 부분을 찾아내서 코딩할 수 있도록 연습해야겠다!!!




<그림 설명>












<나의 코드>


https://github.com/j2wooooo/Daliy_Algorithms/blob/master/Daliy_Algorithms/BOJ_12094/BOJ_12094.cpp


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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include<iostream>
#include<queue>
using namespace std;
 
int N;
int ans;
bool flag;
int board[21][21];
int threshold_value[11];
 
// 최소값을 구하는 함수
int findmaxvalue()
{
    int val = 0;
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            if (board[i][j] > val) val = board[i][j];
        }
    }
    return val;
}
 
// 배열의 값을 복사하는 함수
void copy_arr(int(*temp)[21], int(*board)[21])
{
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            *(temp[i] + j) = *(board[i] + j);
        }
    }
    return;
}
 
// 4방향을 탐색하는 함수
void direct(int dir)
{
    queue<int> q; // board의 값을 넣을 queue
 
    if (dir == 0// UP
    {
        for (int j = 0; j < N; j++)
        {
            for (int i = 0; i < N; i++)
            {
                if (board[i][j]) {
                    // 0 이 아니면 값을 queue에 넣고 board의 값을 0 으로 채운다.
                    q.push(board[i][j]);
                    board[i][j] = 0;
                }
            }
            int idx = 0;
            // 모든 값을 빼내면서
            while (!q.empty())
            {
                int val = q.front();
                q.pop();
 
                // board의 값이 0 이면 그 자리에 queue에 넣었던 값을 넣는다.
                if (!board[idx][j]) board[idx][j] = val;
                // board의 값이 queue의 값과 같다면 두 값을 합쳐 넣고 위치를 한 칸 이동시킨다.
                else if (board[idx][j] == val) board[idx++][j] += val;
                // board의 값이 queue의 값과 다르다면 다음 위치로 한 칸 이동시켜 값을 넣는다.
                else if (board[idx][j]) board[++idx][j] = val;
            }
        }
    }
    else if (dir == 1// DOWN
    {
        for (int j = 0; j < N; j++)
        {
            for (int i = N - 1; i >= 0; i--)
            {
                if (board[i][j]) {
                    q.push(board[i][j]);
                    board[i][j] = 0;
                }
            }
            int idx = N - 1;
            while (!q.empty())
            {
                int val = q.front();
                q.pop();
 
                if (!board[idx][j]) board[idx][j] = val;
                else if (board[idx][j] == val) board[idx--][j] += val;
                else if (board[idx][j]) board[--idx][j] = val;
            }
        }
    }
    else if (dir == 2// LEFT
    {
        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < N; j++)
            {
                if (board[i][j]) {
                    q.push(board[i][j]);
                    board[i][j] = 0;
                }
            }
            int idx = 0;
            while (!q.empty())
            {
                int val = q.front();
                q.pop();
 
                if (!board[i][idx]) board[i][idx] = val;
                else if (board[i][idx] == val) board[i][idx+++= val;
                else if (board[i][idx]) board[i][++idx] = val;
            }
        }
    }
    else if (dir == 3// RIGHT
    {
        for (int i = 0; i < N; i++)
        {
            for (int j = N - 1; j >= 0; j--)
            {
                if (board[i][j]) {
                    q.push(board[i][j]);
                    board[i][j] = 0;
                }
            }
            int idx = N - 1;
            while (!q.empty())
            {
                int val = q.front();
                q.pop();
 
                if (!board[i][idx]) board[i][idx] = val;
                else if (board[i][idx] == val) board[i][idx--+= val;
                else if (board[i][idx]) board[i][--idx] = val;
            }
        }
    }
}
bool compare_arr(int (*temp)[21], int (*board)[21])
{
    for (int j = 0; j < N; j++)
    {
        for (int k = 0; k < N; k++)
        {
            if (temp[j][k] != board[j][k]) return true;
        }
    }
    return false;
}
void dfs(int _cnt)
{
    int val = findmaxvalue();
 
    if (val <= threshold_value[_cnt]) return// 현재 count에서 최대값 만드는게 불가능
 
    // count 10번째까지 오면
    // count마다 가져야할 최소값을 갱신한다.
    if (_cnt == 10) {
        ans = max(ans, val);
        int v = ans;
        while (_cnt > 0) {
            threshold_value[_cnt--= v;
            v /= 2;
        }
        return;
    }
 
    int temp[21][21= { 0 };
    copy_arr(temp, board);
 
    for (int i = 0; i < 4; i++)
    {
        direct(i);
 
        flag = compare_arr(temp, board);
 
        if (flag) {
            dfs(_cnt + 1);
            copy_arr(board, temp);
        }
    }
    return;
}
 
int main()
{
    int cnt = 0;
 
    cin >> N;
 
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            cin >> board[i][j];
        }
    }
 
    ans = findmaxvalue();
 
    dfs(cnt);
 
    cout << ans;
 
    return 0;
}
cs