No.228 ゆきこちゃんの 15 パズル

Quiz

https://yukicoder.me/problems/247

Submit

https://yukicoder.me/submissions/336214

画像

  • テストケース2

f:id:peroon:20190408061909j:plain

解法

  • シミュレーションする
  • 0の置かれている位置にあるべき数字を考える
    • 画像で言えば「12」(3行目4列目なので)
  • そのあるべき数字は上下左右にあるはず
    • なければ解けない (各駒1度しか動かせないため)
  • 交換する
  • これで盤面は最適な状態に一歩近づいた
  • 新しい空白から、同様の処理を繰り返す
  • 動けなくなったら目的の配置になっているかチェックして終了

その他

  • 解説Blogが少なかったので書いた
  • 転倒数で求めるのは嘘解法という記述もあったのでシミュレーションで解いた
  • とはいえ転倒数も面白い https://mathtrain.jp/8puzzle
    • 15パズル
    • あみだくじ

Code

int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};

ll A[4][4];

// そこにあるべき数字
ll calc_number(ll i, ll j){
    ll ret = 4 * i + j + 1;
    if(i==3 && j==3){
        ret = 0;
    }
    return ret;
}

int main(){
    cin.tie(0);
    ios::sync_with_stdio(false);

    // input
    FOR(i, 0, 4){
        cin >> A[i][0];
        cin >> A[i][1];
        cin >> A[i][2];
        cin >> A[i][3];
    }

    ll empty_i;
    ll empty_j;

    FOR(i, 0, 4){
        FOR(j, 0, 4){
            if(A[i][j]==0){
                empty_i = i;
                empty_j = j;
            }
        }
    }

    while(true){

        bool found = false;

        // 上下左右を見る
        FOR(i, 0, 4){
            // 空白部分にあるべき数字
            ll number = calc_number(empty_i, empty_j);

            ll next_i = empty_i + dy[i];
            ll next_j = empty_j + dx[i];
            
            if(0<=next_i && next_i < 4 && 0<=next_j && next_j < 4 && A[next_i][next_j]==number){
                swap(A[empty_i][empty_j], A[next_i][next_j]);
                empty_i = next_i;
                empty_j = next_j;
                found = true;
            }
        }

        // 4方向見ても見つからなかった
        if(!found){
            goto HERE;
        }
    }

    HERE:

    // last check
    FOR(i, 0, 4){
        FOR(j, 0, 4){
            if(A[i][j]!=calc_number(i, j)){
                p_no();
                return 0;
            }
        }
    }

    p_yes();
    return 0;
}