
보드의 크기와 보드판의 블록 상태가 주어졌을 때, 최대 10번 이동해서 만들 수 있는 가장 큰 블록의 값을 구하는 프로그램을 작성하시오.
#include <iostream>
#include <queue>
using namespace std;
int N, ans;
int board[20][20];
int maxPerDepth[11];
void action(int actionSelector) {
    queue<int> q;
    switch(actionSelector) {
    case 0: // 왼쪽으로 움직이는 경우
        for (int y = 0; y < N; ++y) {
            for (int x = 0; x < N; ++x) {
                if (board[y][x]) q.push(board[y][x]);
                board[y][x] = 0;
            }
            int curRowIdx = 0;      // 현재 i번째 행의 인덱스
            while (!q.empty()) {
                int tempVar = q.front(); q.pop();
                if (board[y][curRowIdx] == 0) board[y][curRowIdx] = tempVar;
                else if (board[y][curRowIdx] == tempVar) board[y][curRowIdx++] += tempVar;
                else board[y][++curRowIdx] = tempVar;
            }
        }
        break;
    case 1: // 오른쪽으로 움직이는 경우
        for (int y = 0; y < N; ++y) {
            for (int x = N - 1; x >= 0; --x) {
                if (board[y][x]) q.push(board[y][x]);
                board[y][x] = 0;
            }
            int curRowIdx = N - 1;  // 우측 끝 인덱스부터 큐에 저장한다.
            while (!q.empty()) {
                int tempVar = q.front(); q.pop();
                if (board[y][curRowIdx] == 0) board[y][curRowIdx] = tempVar;
                else if (board[y][curRowIdx] == tempVar) board[y][curRowIdx--] += tempVar;
                else board[y][--curRowIdx] = tempVar;
            }
        }
        break;
    case 2: // 위쪽으로 움직이는 경우
        for (int x = 0; x < N; ++x) {
            for (int y = 0; y < N; ++y) {
                if (board[y][x]) q.push(board[y][x]);
                board[y][x] = 0;
            }
            int curColIdx = 0;      // 현재 i번째 열의 인덱스
            while (!q.empty()) {
                int tempVar = q.front(); q.pop();
                if (board[curColIdx][x] == 0) board[curColIdx][x] = tempVar;
                else if (board[curColIdx][x] == tempVar) board[curColIdx++][x] += tempVar;
                else board[++curColIdx][x] = tempVar;
            }
        }
        break;
    case 3: // 아래쪽으로 움직이는 경우
        for (int x = 0; x < N; ++x) {
            for (int y = N - 1; y >= 0; --y) {
                if (board[y][x]) q.push(board[y][x]);
                board[y][x] = 0;
            }
            int curColIdx = N - 1;
            while (!q.empty()) {
                int tempVar = q.front(); q.pop();
                if (board[curColIdx][x] == 0) board[curColIdx][x] = tempVar;
                else if (board[curColIdx][x] == tempVar) board[curColIdx--][x] += tempVar;
                else board[--curColIdx][x] = tempVar;
            }
        }
        break;
    }
}
bool isSame(int tmp[20][20]) {
	for (int y = 0; y < N; ++y) 
        for (int x = 0; x < N; ++x)
			if (tmp[y][x] != board[y][x]) return false;
	return true;
}
int findMaxElement() {
    int var = 0;
    for (int y = 0; y < N; ++y) 
        for (int x = 0; x < N; ++x)
            var = max(var, board[y][x]);
    return var;
}
void copyArray(int lhs[][20], int rhs[][20]) {
    for (int y = 0; y < N; ++y)
        for (int x = 0; x < N; ++x)
            lhs[y][x] = rhs[y][x];
}
void solve(int depth) {
    // [기저 1] - 현재 게임판에서 최대 원소 추출하고, 
    // 현재 depth에서 만들어질 것으로 예상되는 최대 원소보다 작거나 같다면 탐색 불필요.
    int checkCurrMaxValue = findMaxElement();   
    if (checkCurrMaxValue <= maxPerDepth[depth]) return; 
    // [기저 2] - 10번 움직였다면, 최대값 구해서 깊이 별 최대 원소값 갱신한다.
    if (depth == 10) {
        ans = max(ans, checkCurrMaxValue);
        int expectMaxPerDepth = ans;
        while (depth > 0) {
            maxPerDepth[depth--] = expectMaxPerDepth;
            expectMaxPerDepth /= 2;
        }
        return;
    }   
    int currBoard[20][20];
    copyArray(currBoard, board);    // 현재 depth의 게임판을 저장한다.
    for (int i = 0; i < 4; ++i) {   // 취할 수 있는 행동은 4가지
        action(i);                  // 게임판을 움직인 뒤, 변화가 없다면 다음 액션으로 진행.
        if (isSame(currBoard)) continue;
        solve(depth + 1);           // 다음 depth로 이동하고,
        copyArray(board, currBoard);// 완전탐색 원칙에 따라 원상복귀 후 다음 경우의 수(액션) 진행.
    }
}
int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    cin >> N;   // 보드의 크기 (N X N)
    for (int y = 0; y < N; ++y)
        for (int x = 0; x < N; ++x) 
            cin >> board[y][x];
    ans = findMaxElement();     // ans의 초기값 할당 (안해주면 100%에서 오답. (반례: 1x1 칸일 경우))
    solve(0);
    cout << ans << '\n';
}
