D - パズル

[Q] https://atcoder.jp/contests/indeednow-quala/tasks/indeednow_2015_quala_4

スライドパズルの最適解を探索する問題。
AOJで全く同じ問題をみんなで解いたことがありました。
[Q] https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ALDS1_13_C

バックトラックで全探索。4^26通り検証しつつ、3つの枝切りを考えます。
1. 前の手と同じ動作を省略

2. 今の盤面と正解盤面で距離を比較して、明らかに残り手数で完成しないなら打ち切る。

(3. 少ない手数から、だんだん大きくしていく。)
手数1で整うと仮定して4^1をみる。
ダメだったら手数2で整うと仮定して4^2を見る。
・・・
ダメだったら手数26で整うと仮定して4^26を見る。
いきなり4^26を見に行くより、軽いケースで探索が早くなる。

実装。146ms。
1msで終わっている人がたくさんいてすごい。

ll H, W, sy, sx;
ll C[6][6];

ll eval(){
 ll ret = 0;
 rep(i, H) rep(j, W){
   ll c = C[i][j];
   if (c==-1) c=H*W-1;
   ret += abs(c/W-i) + abs(c%W-j); // 正解盤面とのマンハッタン距離
 }

 return ret;
}

bool dfs(ll depth, ll fin, ll y, ll x, ll py, ll px){
 ll dif = eval();
 if (dif == 0) return true;
 if ((fin-depth)*2 < dif) return false; // 1手スライドさせたら、距離が2縮まる。
 rep(k, 4){
   ll ny = y+dy[k];
   ll nx = x+dx[k];
   if (ny<0 || ny>=H || nx<0 || nx>=W) continue;
   if (ny==py && nx==px) continue; // 前の手は見ない
   swap(C[y][x], C[ny][nx]); // バックトラック
   if (dfs(depth+1, fin, ny, nx, y, x)) return true; // 1回答えが見つかったら終了
   swap(C[y][x], C[ny][nx]); // 復元
 }
 return false;
}

int main(){
 cincout();
 
 cin >> H >> W;
 rep(i, H) rep(j, W){
   cin >> C[i][j];
   --C[i][j];
   if (C[i][j] == -1){
     sy=i;
     sx=j;
   }
 }

 for(ll i=0; i<=24; ++i){ // あがり手数を仮定
   if (dfs(0, i, sy, sx, -1, -1)){ // 1回答えが見つかったら終了
     cout << i << endl;
     return 0;
   }
 }
 cout << -1 << endl;
}

https://atcoder.jp/contests/indeednow-quala/submissions/26022674


この記事が気に入ったらサポートをしてみませんか?