BOJ 12094

2048(Hard)

문제출처

문제 해석

  • 2048 게임 중, 최대 10번 이동시켜서 얻을 수 있는 가장 큰 블록을 출력하는 문제입니다.

설계(X)

  • 2048(Easy)문제와 같은 로직으로 구현이 되지만, 최대 10번을 이동시켜야 한다는 점이 다릅니다.
  • 완전탐색으로는 시간초과가 날 것이므로 최적화가 필요합니다.
  • 먼저, 블록의 개수를 카운트하는 방식으로 접근했습니다.
  • 원래 가지고 있는 블록의 개수보다 이동했을 때 블록의 개수가 더 적으면 이동시키도록하였습니다.
  • 만약 이전의 블록 개수와 같은 경우, flag를 켜서 한번만 허용하도록 했습니다.
  • 결과는 WA로, 반례가 있는 것 같아서 다른 방법을 생각해야 했습니다.
  • 첫번째로 생각할 수 있었던 것은 모양의 상태의 변화를 보고 이동시키는 방법입니다.
  • 이동시킨 후, 이동시키기 전의 상태와 비교해서 같은 상태이면 해당 방향으로 이동시키기 않는 방식입니다.
  • 위의 방법으로 최적화하였지만 TLE가 나왔습니다.
  • 무언가 더 최적화할 만한 것이 필요해 보였으나, 쉽게 생각하지 못하였습니다.
  • 다른 분들의 블로그를 참조하였습니다.
  • 이동시켰을 때, 나온 최대 크기의 블록을 남은 이동횟수를 이동시켰을 때 나올 수 있는 최대의 기댓값을 비교하는 방식입니다.
  • 현재의 최댓값보다 다음으로 이동한 상태의 최대 블록값의 최대 기댓값이 작거나 같으면, 나머지 10번을 모두 이동시켜도 현재의 최댓값보다 작거나 같을 것이 자명하기 때문에 더이상 이동시키지 않습니다.

구현(X)

코드
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
#define MAXN 20
using namespace std;

int map[MAXN][MAXN];

int N;
int ans ;
int move(int mode) {
    int max_num = 0;
    // right
    if (mode == 0) {
        for (int i = 0; i < N; i++) {
            queue<int> q;
            for (int j = N - 1; j >= 0; j--) {
                if (map[i][j] == 0) continue;
                q.push(map[i][j]);
                map[i][j] = 0;
            }
            int idx = N - 1;
            while (!q.empty()) {
                int now = q.front();
                q.pop();
                if (map[i][idx] == 0) {
                    map[i][idx] = now;
                    max_num = max(max_num,map[i][idx]);
                }
                else if (map[i][idx] == now) {
                    map[i][idx] *= 2;
                    max_num = max(max_num,map[i][idx]);
                    idx--;
                }
                else {
                    map[i][idx - 1] = now;
                    max_num = max(max_num,map[i][idx-1]);
                    idx--;
                }
            }
        }
    }
    // left
    else if (mode == 1) {
        for (int i = 0; i < N; i++) {
            queue<int> q;
            for (int j = 0; j <  N; j++) {
                if (map[i][j] == 0) continue;
                q.push(map[i][j]);
                map[i][j] = 0;
            }
            int idx = 0;
            while (!q.empty()) {
                int now = q.front();
                q.pop();
                if (map[i][idx] == 0) {
                    map[i][idx] = now;
                    max_num = max(max_num,map[i][idx]);
                }
                else if (map[i][idx] == now) {
                    map[i][idx] *= 2;
                    max_num = max(max_num,map[i][idx]);
                    idx++;
                }
                else {
                    map[i][idx + 1] = now;
                    max_num = max(max_num,map[i][idx+1]);
                    idx++;
                }
            }
        }
    }
    // up
    else if (mode == 2) {
        for (int j = 0; j < N; j++) {
            queue<int> q;
            for (int i = 0; i < N; i++) {
                if (map[i][j] == 0) continue;
                q.push(map[i][j]);
                map[i][j] = 0;
            }
            int idx = 0;
            while (!q.empty()) {
                int now = q.front();
                q.pop();
                if (map[idx][j] == 0) {
                    map[idx][j] = now;
                    max_num = max(max_num,map[idx][j]);
                }
                else if (map[idx][j] == now) {
                    map[idx][j] *= 2;
                    max_num = max(max_num,map[idx][j]);
                    idx++;
                }
                else {
                    map[idx+1][j] = now;
                    max_num = max(max_num,map[idx+1][j]);
                    idx++;
                }
            }
        }
    }
    // down
    else if (mode == 3) {
        for (int j = 0; j < N; j++) {
            queue<int> q;
            for (int i = N-1; i >= 0; i--) {
                if (map[i][j] == 0) continue;
                q.push(map[i][j]);
                map[i][j] = 0;
            }
            int idx = N-1;
            while (!q.empty()) {
                int now = q.front();
                q.pop();
                if (map[idx][j] == 0) {
                    map[idx][j] = now;
                    max_num = max(max_num,map[idx][j]);
                }
                else if (map[idx][j] == now) {
                    map[idx][j] *= 2;
                    max_num = max(max_num,map[idx][j]);
                    idx--;
                }
                else {
                    map[idx-1][j] = now;
                    max_num = max(max_num,map[idx-1][j]);
               idx--;
            }

         }
        }
    }
    return max_num;
}
bool check(int copy_map[][MAXN]){
    for (int i = 0; i<N; i++){
        for (int j= 0 ;j <N; j++){
            if (copy_map[i][j] != map[i][j]) return false;
        }
    }
    return true;
}
void dfs(int now, int num) {
    ans = max(ans,num);
    if (now ==10) {
        return;
    }
    int copy_map[MAXN][MAXN];
    memcpy(copy_map,map,sizeof(map));
    for (int i = 0; i < 4; i++) {
        int max_num = move(i);
        if (check(copy_map)) continue;
        if (!(max_num*(1<<(9-now))<=ans)) dfs(now + 1,max_num);
        memcpy(map,copy_map,sizeof(copy_map));
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> N;
    int num = 0;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            cin >> map[i][j];
            num = max(num,map[i][j]);
        }
    }
    ans = num;
    dfs(0,0);
    cout << ans << "\n";
    return 0;
}
디버깅
제출결과
  • 892ms

마무리

  • 가지치기를 아주 잘 해야하는 백트래킹 문제였습니다. 1초에 가까운 제출결과가 나온것으로 보아, 더 나은 가지치기 방법이 있을 것으로 보입니다.