ABOUT ME

IT에 관한 다양한 정보를 공유하는 블로그입니다.

Today
Yesterday
Total
  • #12094. 2048 (Hard)
    Code/BOJ 2020. 3. 6. 23:26
    728x90
    반응형

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

     

    12094번: 2048 (Hard)

    첫째 줄에 보드의 크기 N (1 ≤ N ≤ 20)이 주어진다. 둘째 줄부터 N개의 줄에는 게임판의 초기 상태가 주어진다. 0은 빈 칸을 나타내며, 이외의 값은 모두 블록을 나타낸다. 블록에 쓰여 있는 수는 2보다 크거나 같고, 1024보다 작거나 같은 2의 제곱꼴이다. 블록은 적어도 하나 주어진다.

    www.acmicpc.net

     

    문제

    해당 링크:https://play2048.co/를 접속하면 2048게임을 할 수 있다.

    이 게임에서 한 번의 이동은 보드 위에 있는 전체 블록을 상하좌우 네 방향 중 하나로 이동시키는 것이다. 이때, 같은 값을 갖는 두 블록이 충돌하면 두 블록은 하나로 합쳐지게 된다. 한 번의 이동에서 이미 합쳐진 블록은 또 다른 블록과 다시 합쳐질 수 없다. (실제 게임에서는 이동을 한 번 할 때마다 블록이 추가되지만, 이 문제에서 블록이 추가되는 경우는 없다)

    마지막으로, 똑같은 수가 세 개가 있는 경우에는 이동하려고 하는 쪽의 칸이 먼저 합쳐진다. 예를 들어, 위로 이동시키는 경우에는 위쪽에 있는 블록이 먼저 합쳐지게 된다. <그림 14>의 경우에 위로 이동하면 <그림 15>를 만든다.

    이 문제에서 다루는 2048 게임은 보드의 크기가 N×N 이다. 보드의 크기와 보드판의 블록 상태가 주어졌을 때, 최대 10번 이동해서 만들 수 있는 가장 큰 블록의 값을 구하는 프로그램을 작성하시오.

     

    입력

    첫째 줄에 보드의 크기 N (1 ≤ N ≤ 20)이 주어진다. 둘째 줄부터 N개의 줄에는 게임판의 초기 상태가 주어진다. 0은 빈 칸을 나타내며, 이외의 값은 모두 블록을 나타낸다. 블록에 쓰여 있는 수는 2보다 크거나 같고, 1024보다 작거나 같은 2의 제곱꼴이다. 블록은 적어도 하나 주어진다.

     

    출력

    최대 10번 이동시켜서 얻을 수 있는 가장 큰 블록을 출력한다.

     

    풀이

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

     

    시간을 줄이는 방법에는 두가지가 핵심이다.

    1. 이동 후 보드 모양이 변하지 않는 경우

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

     

    위에 두가지중 하나라도 만족한다면 다음 동작을 굳이 할 필요가 없어진다. 그리고 2번째 방법을 좀 더 설명하자면 처음에 10번 움직였을때 최댓값이 32가 나왔다고 생각해보자. 그렇다면 9번 움직였을때는 적어도 16이 나왔어야 하고 8번 움직였을때는 8, 7번 움직였을때는 4, 6번 움직였을 떄는 2가 나와야 32라는 숫자를 만들 수 있다. 그래서 이 숫자들을 dp[cnt(몇 번 움직였는지에 대한 카운트)]=해당 턴에서 나올 수 있는 최댓 값을 저장하는 배열에 남긴다.

     

    그래서 그 다음번에 다른 보드모양에서 현재 턴의 MAX값이 dp[현재턴]에 저장되어 있는 값보다 작을 경우 그다음 동작을 수행할 필요가 없어진다. (이것이 핵심!!)

     

    이 2가지를 재귀함수로 잘 표현해주면 된다.

     

    코드

     

    // #12094 2048 (hard)
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    // 개수 cnt 쟤서 줄면 visit true 와 마찬가지아닐까?!!
    using namespace std;
    int n;
    int dir[4][2] = { {1,0}, { 0,1 }, { -1,0 }, { 0,-1 } };//하우상좌
    int arr[21][21];
    int dp[11];// cnt, d방향으로 이동했을때 max값 저장 dp
    int ans;
    // 블럭을 옮기는 함수
    int move_block(int d) {
    int idx;
    queue<int> q;
    int cparr[21][21];
    memset(cparr, 0, sizeof(cparr));
    if (d == 0) {//아래로 이동
    for (int j = 0; j < n; j++) {
    idx = n - 1;
    for (int i = n - 1; i >= 0; i--) {
    if (arr[i][j] != 0)q.push(arr[i][j]);
    }
    while (!q.empty()) {
    int x = q.front();
    if (cparr[idx][j] == 0) {
    cparr[idx][j] = x;
    q.pop();
    }
    else {
    if (cparr[idx][j] == x) {
    cparr[idx][j] += x;
    q.pop();
    }
    idx--;
    }
    }
    }
    }
    else if (d == 2) {//위로 이동
    for (int j = 0; j < n; j++) {
    idx = 0;
    for (int i =0; i< n; i++) {
    if (arr[i][j] != 0)q.push(arr[i][j]);
    }
    while (!q.empty()) {
    int x = q.front();
    if (cparr[idx][j] == 0) {
    cparr[idx][j] = x;
    q.pop();
    }
    else {
    if (cparr[idx][j] == x) {
    cparr[idx][j] += x;
    q.pop();
    }
    idx++;
    }
    }
    }
    }
    else if (d == 1) {//오른쪽으로 이동
    for (int i = 0; i < n; i++) {
    idx = n - 1;
    for (int j = n - 1; j >= 0; j--) {
    if (arr[i][j] != 0)q.push(arr[i][j]);
    }
    while (!q.empty()) {
    int x = q.front();
    if (cparr[i][idx] == 0) {
    cparr[i][idx] = x;
    q.pop();
    }
    else {
    if (cparr[i][idx] == x) {
    cparr[i][idx] += x;
    q.pop();
    }
    idx--;
    }
    }
    }
    }
    else if (d == 3) {//왼쪽으로 이동
    for (int i = 0; i < n; i++) {
    idx = 0;
    for (int j = 0; j< n; j++) {
    if (arr[i][j] != 0)q.push(arr[i][j]);
    }
    while (!q.empty()) {
    int x = q.front();
    if (cparr[i][idx] == 0) {
    cparr[i][idx] = x;
    q.pop();
    }
    else {
    if (cparr[i][idx] == x) {
    cparr[i][idx] += x;
    q.pop();
    }
    idx++;
    }
    }
    }
    }
    memcpy(arr, cparr, sizeof(arr));
    int ret = 0;
    for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
    ret = max(ret, arr[i][j]);
    }
    }
    return ret;
    }
    bool cmp(int a[21][21], int b[21][21]){
    for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
    if (a[i][j] != b[i][j])return true;
    }
    }
    return false;
    }
    // dfs 최대 5번까지
    void dfs(int cnt,int sum) {
    if (sum <= dp[cnt])return;
    // 블럭을 옮겨주고 만약 카운트 변화가 안생기면 continue
    if (cnt == 10) {
    ans = max(ans, sum);
    while (cnt > 0) {
    dp[cnt--] = sum;
    sum /= 2;
    }
    return;
    }
    int map[21][21];
    memcpy(map, arr, sizeof(map));
    for (int d = 0; d < 4; d++) {
    int MAX=move_block(d);
    if (cmp(arr, map)) {
    dfs(cnt + 1, MAX);
    memcpy(arr, map, sizeof(arr));
    }
    }
    return;
    }
    int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
    cin >> arr[i][j];
    }
    }
    int MAX = 0;
    for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
    MAX = max(MAX, arr[i][j]);
    }
    }
    ans = max(ans, MAX);
    dfs(0,MAX);
    cout << ans<< endl;
    //cout << ans << endl;
    }

     

    728x90
    반응형

    'Code > BOJ' 카테고리의 다른 글

    #2610 회의준비  (0) 2020.03.07
    #10282 해킹  (0) 2020.03.07
    #16235 나무 재테크  (0) 2020.03.06
    #17143 낚시왕  (0) 2020.03.06
    #1113 수영장 만들기  (0) 2020.03.05
Designed by Tistory.