这是我使用简单的BFS实现。Dijkstra也可以(用按成本降序排序的stl::priority_queue
替换stl::queue
),但会严重超标。
需要注意的是,我们实际上在搜索一个图,其节点并不完全对应于给定数组中的单元格。为了得到该图,我使用了基于DFS的简单泛洪算法(你也可以使用BFS,但DFS对我来说稍微短一些)。它的作用是找到所有连接的、相同字符的组件,并将它们分配给相同的颜色/节点。因此,在泛洪之后,我们可以通过查看colour[row][col]
的值来找出每个单元格属于底层图中的哪个节点。然后,我只需迭代单元格,并找出所有相邻单元格颜色不相同(即位于不同节点)的单元格。这些就是我们图的边缘。我在迭代单元格时维护一个stl::set
的边缘,以消除重复的边缘。之后,只需从边缘列表构建邻接表,我们就可以进行BFS了。
代码(使用C++):
#include <queue>
#include <vector>
#include <iostream>
#include <string>
#include <set>
#include <cstring>
using namespace std;
#define SIZE 1001
vector<string> board;
int colour[SIZE][SIZE];
int dr[]={0,1,0,-1};
int dc[]={1,0,-1,0};
int min(int x,int y){ return (x<y)?x:y;}
int max(int x,int y){ return (x>y)?x:y;}
void dfs(int r, int c, int col, vector<string> &b){
if (colour[r][c]<0){
colour[r][c]=col;
for(int i=0;i<4;i++){
int nr=r+dr[i],nc=c+dc[i];
if (nr>=0 && nr<b.size() && nc>=0 && nc<b[0].size() && b[nr][nc]==b[r][c])
dfs(nr,nc,col,b);
}
}
}
int flood_fill(vector<string> &b){
memset(colour,-1,sizeof(colour));
int current_node=0;
for(int i=0;i<b.size();i++){
for(int j=0;j<b[0].size();j++){
if (colour[i][j]<0){
dfs(i,j,current_node,b);
current_node++;
}
}
}
return current_node;
}
vector<vector<int> > build_graph(vector<string> &b){
int total_nodes=flood_fill(b);
set<pair<int,int> > edge_list;
for(int r=0;r<b.size();r++){
for(int c=0;c<b[0].size();c++){
for(int i=0;i<4;i++){
int nr=r+dr[i],nc=c+dc[i];
if (nr>=0 && nr<b.size() && nc>=0 && nc<b[0].size() && colour[nr][nc]!=colour[r][c]){
int u=colour[r][c], v=colour[nr][nc];
if (u!=v) edge_list.insert(make_pair(min(u,v),max(u,v)));
}
}
}
}
vector<vector<int> > graph(total_nodes);
for(set<pair<int,int> >::iterator edge=edge_list.begin();edge!=edge_list.end();edge++){
int u=edge->first,v=edge->second;
graph[u].push_back(v);
graph[v].push_back(u);
}
return graph;
}
int bfs(vector<vector<int> > &G, int start, int end){
vector<int> cost(G.size(),-1);
queue<int> Q;
Q.push(start);
cost[start]=0;
while (!Q.empty()){
int node=Q.front();Q.pop();
vector<int> &adj=G[node];
for(int i=0;i<adj.size();i++){
if (cost[adj[i]]==-1){
cost[adj[i]]=cost[node]+1;
Q.push(adj[i]);
}
}
}
return cost[end];
}
int main(){
string line;
int rows,cols;
cin>>rows>>cols;
for(int r=0;r<rows;r++){
line="";
char ch;
for(int c=0;c<cols;c++){
cin>>ch;
line+=ch;
}
board.push_back(line);
}
vector<vector<int> > actual_graph=build_graph(board);
cout<<bfs(actual_graph,colour[0][0],colour[rows-1][cols-1])<<"\n";
}
这只是一个快速的hack,还有很多改进可以做。但我认为它在运行时复杂度方面已经非常接近最优,并且应该足够快地运行数千个板子(不要忘记更改SIZE
的#define
)。此外,我只测试了您提供的一个案例。所以,正如Knuth所说,“小心上述代码中的错误;我只证明了它的正确性,而没有尝试过。” :).