ダイクストラを使用します。
あなたが扱っているのはテキスト ベースのゲームなので、1000 x 1000 文字を超えるマップについて話しているとは考えにくいと思います。O(n*logn)
これにより、非常に低コストで、非常にシンプルでわかりやすいコードで、保証された最良の回答が得られます。
基本的に、各検索状態は 2 つのことを追跡する必要があります:これまでにいくつの壁を通り抜けたか、および通常の空きスペースはいくつありますか。これは、たとえば、各壁を通過するコストが 2^16 であると仮定することにより、検索とマークマトリックスの両方で単一の整数にエンコードできます。したがって、ダイクストラの自然な順序付けにより、壁が最も少ないパスが最初に試行され、壁を通過した後、同じ数の壁を通過せずに既に到達したパスを繰り返さないことが保証されます。
基本的に、32 ビット整数を仮定すると、5 つの空きスペースと 3 つの壁を通過した状態は次のようになります
0000000000000011 0000000000000101
。マップが非常に巨大で、迷路のようで、何トンもの壁がある場合、壁が少ない場合などは、この表現を微調整して、各情報に使用するビット数を増減したり、より快適に感じる場合はより長い整数を使用したりすることもできます。この特定のエンコード例は、65,000 を超える空きスペースを歩く必要がある最短パスが存在する場合、「オーバーフロー」します。
2 つではなく 1 つの整数を使用する主な利点 (壁/空きスペース) はint mark[MAXN][MAXM];
、検索を追跡するために単一の単純なマトリックスを使用できることです。壁を通り抜けて特定の正方形に到達した場合5
、無用な状態の伝播を防ぐために、4 つ、3 つ、またはそれ以下の壁で到達できたかどうかを確認する必要はありません。この情報は自動的に整数に埋め込まれます。の量をより高いビットに保存する限り、walls
「壁のコスト」が高くてもパスを繰り返すことはありません。
これは C++ で完全に実装されたアルゴリズムです。上記のアイデアを視覚化して理解するための疑似コードと考えてください :)
int rx[4] = {1,0,-1,0};
int ry[4] = {0,1,0,-1};
int text[height][width]; // your map
int mark[height][width]; //set every position to "infinite" cost
int parent[height][width]; //to recover the final path
priority_queue<int64_t, vector<int64_t>, greater<int64_t> > q;
int64_t state = (initial_y<<16) + initial_x;
q.push(state);
while(!q.empty()){
state = q.top(); q.pop();
int x = state & 0xFF;
int y = (state>>16) & 0xFF;
int cost = state>>32;
if(cost > mark[x][y]) continue;
if(text[x][y] == 'X') break;
for(int i = 0; i < 4; ++i){
int xx = x+rx[i];
int yy = y+ry[i];
if(xx > -1 && xx < width && yy > -1 && yy < height){
int newcost = cost;
if(text[yy][xx] == ' ') newcost += 1;
else newcost += 1<<16;
if(newcost < mark[yy][xx]){
mark[yy][xx] = newcost;
parent[yy][xx] = i; //you know which direction you came from
q.push( ((int64_t)newcost << 32) + (y<<16) + x);
}
}
}
}
// The number of walls in the final answer:
// walls = state>>48;
// steps = (state>>32) & 0xFF; // (non walls)
// you can recover the exact path traversing back using the information in parent[][]