Official

G - 8 Puzzle on Graph Editorial by leaf1415


\(8\) 個のコマの配置を \(9\) 文字の文字列 \(s_1 s_2 \ldots s_9\)で 表現することを考えます。 頂点 \(i\) にコマ \(j\) が置かれているとき \(s_i\)\(j\) とします。また、頂点 \(i\) が空の頂点のときは \(s_i\)\(9\) とします。
例えば、完成したパズルのコマの配置は文字列 123456789 で表されます。また、本問題の入力例 \(1\) の説明文に示す手順では、コマの配置が 931456782 \(\rightarrow\) 231456789 \(\rightarrow\) 291456783 \(\rightarrow\) 921456783 \(\rightarrow\) 129456783 \(\rightarrow\) 123456789 と変化します。

コマの配置は 1 から 9 の文字の並べ替えで表されるため、コマの配置の総数は \(9!\) 個です。
また、ある状態 \(s_1 s_2 \ldots s_9\) からは、「 \(s_i = \) 9または \(s_j = \)9 」 かつ 「頂点 \(i\) と頂点 \(j\) を結ぶ辺が存在する」ときかつそのときに限り、\(s_i\)\(s_j\) を入れ替えた状態 \(s_1 \ldots s_{i-1} s_j s_{i+1} \ldots s_{j-1} s_i s_{j+1} \ldots s_9\)\(1\) 回の操作で遷移することができます。

ありうる \(9!\) 個の状態を頂点とし、\(1\) 回の操作で遷移可能な状態どうしの間に辺を張った無向グラフを考えると、本問題の答えはこの無向グラフ上での「初期状態から状態 123456789 までの最短距離(到達するためにたどる辺の本数の最小値)」です。
これは、このグラフ上で幅優先探索(BFS)を行うことで求めることができます。 幅優先探索の過程で「初期状態から各状態 \(s_1 s_2 \ldots s_9\) への最短距離」を記憶するためには、文字列 \(s_1 s_2 \ldots s_9\) に対して最短距離を対応させる連想配列を用いることができます。

以下に、C++言語による実装例を記載します。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
using namespace std;

int main(void)
{
  int m;
  cin >> m;

  vector<int> G[10];
  int u, v;
  for(int i = 1; i <= m; i++){
    cin >> u >> v;
    G[u].push_back(v);
    G[v].push_back(u);
  }

  int p; string s = "999999999";
  for(int i = 1; i <= 8; i++){
    cin >> p;
    s[p-1] = i + '0';
  }

  queue<string> Q;
  Q.push(s);
  map<string, int> mp;
  mp[s] = 0;

  while(Q.size()){
    string s = Q.front(); Q.pop();
    for(int i = 1; i <= 9; i++) if(s[i-1] == '9') v = i;

    for(auto u : G[v]){
      string t = s;
      swap(t[u-1], t[v-1]);
      if(mp.count(t)) continue;
      mp[t] = mp[s] + 1;
      Q.push(t);
    }
  }
  if(mp.count("123456789") == 0) cout << -1 << endl;
  else cout << mp["123456789"] << endl;

  return 0;
}

posted:
last update: