使用 Dijkstra。
由于您正在处理基于文本的游戏,我发现您谈论的地图不太可能大于 1000 x 1000 个字符。这将以非常低的成本O(n*logn)
和非常简单直接的代码为您提供有保证的最佳答案。
基本上,每个搜索状态都必须跟踪两件事:到目前为止你已经穿过了多少墙,以及有多少常规的空白空间。对于搜索和标记矩阵,可以将其编码为单个整数,例如假设每面墙的遍历成本为 2^16。因此,Dijkstra 的自然排序将确保首先尝试具有最少墙壁的路径,并且在穿过墙壁之后,您不会重复您已经到达的路径而没有穿过尽可能多的墙壁。
基本上,假设一个 32 位整数,一个经过 5 个空白空间和 3 个墙壁的状态将如下所示
0000000000000011 0000000000000101
:如果你的地图真的很大,像迷宫一样,有很多墙,很少有墙等等,你可以调整这个表示来为每个信息使用更多或更少的位,或者如果你觉得更舒服,甚至可以使用更长的整数,如如果存在需要穿过超过 65,000 个空白空间的最短路径,则此特定编码示例将“溢出”。
使用单个整数而不是两个整数(对于墙壁/空白空间)的主要优点是您可以拥有一个简单的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[][]