我的任务是找出矩阵中一个点到另一个点的最短路径,且只能按照上下左右移动
0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 F 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 S 0 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0
S - 起点
F - 终点
0 - 可以穿过的点
1 - 不能穿过的点
很明显广度优先搜索是解决该问题的最佳方法,我知道Boost库支持该算法,但是我对Boost库不是特别熟悉。
我如何使用Boost库的广度优先搜索算法解决我的问题?据我了解,Boost库的广度优先搜索算法是专门为图而设计的,我感觉把m*n的矩阵转换成一个包含m*n个点和m*(n-1) + (m-1)*n条边的图不是一个好主意
我可以直接将Boost库的广度优先搜索算法应用于矩阵(不是转换成图)吗,或者是能使用我自己定义的函数就更好了?
注:以上是对提问者的翻译
以下是对回答者的翻译:
(首先很抱歉给的答案篇幅这么长。我使用Boost Graph Library已经有些日子了,写这些也可以很好的复习一下。全部代码在最底部)
Boost图形库(和通常的泛型编程)的好处就在于你不需要在既定的算法中使用特定的数据结构。你上述提到的遍历矩阵的规则已经和图差不多了,你需要做的就是将这些规则归纳成可以被BGL(Boost图形库)算法使用的traits class。
明确的说,就是我们要为你的graph定义一个特定的boost::graph_traits<T>
结构。让我们假定你的矩阵是一个以行优先的int型的一维数组。但很不幸的是,只包含int[N]的graph_traits
是不够的,因为它没有提供矩阵中维度的信息。所以我们按照以下形式定义你的graph:
namespace matrix { typedef int cell; static const int FREE = 0; static const int WALL = 1; template< size_t ROWS, size_t COLS > struct graph { cell cells[ROWS*COLS]; }; }
我这里使用了一个数组来表示单元格数据,如果在外面管理这些数据的话你可以使用指针更简单一些。现在我们已经有了一个可以用于graph_traits
的表示矩阵的类型了,但是首先让我们定义一些我们需要的类型和函数:
顶点类型和辅助函数:
namespace matrix { typedef size_t vertex_descriptor; template< size_t ROWS, size_t COLS > size_t get_row( vertex_descriptor vertex, graph< ROWS, COLS > const & ) { return vertex / COLS; } template< size_t ROWS, size_t COLS > size_t get_col( vertex_descriptor vertex, graph< ROWS, COLS > const & ) { return vertex % COLS; } template< size_t ROWS, size_t COLS > vertex_descriptor make_vertex( size_t row, size_t col, graph< ROWS, COLS > const & ) { return row * COLS + col; } }
遍历顶点的类型和函数:
namespace matrix { typedef const cell * vertex_iterator; template< size_t ROWS, size_t COLS > std::pair< vertex_iterator, vertex_iterator > vertices( graph< ROWS, COLS > const & g ) { return std::make_pair( g.cells, g.cells + ROWS*COLS ); } typedef size_t vertices_size_type; template< size_t ROWS, size_t COLS > vertices_size_type num_vertices( graph< ROWS, COLS > const & g ) { return ROWS*COLS; } }
边类型:
namespace matrix { typedef std::pair< vertex_descriptor, vertex_descriptor > edge_descriptor; bool operator==( edge_descriptor const & lhs, edge_descriptor const & rhs ) { return lhs.first == rhs.first && lhs.second == rhs.second || lhs.first == rhs.second && lhs.second == rhs.first; } bool operator!=( edge_descriptor const & lhs, edge_descriptor const & rhs ) { return !(lhs == rhs); } }
最后,迭代器和函数来帮助我们遍历出顶点和边存在的关联关系:
namespace matrix { template< size_t ROWS, size_t COLS > vertex_descriptor source( edge_descriptor const & edge, graph< ROWS, COLS > const & ) { return edge.first; } template< size_t ROWS, size_t COLS > vertex_descriptor target( edge_descriptor const & edge, graph< ROWS, COLS > const & ) { return edge.second; } typedef boost::shared_container_iterator< std::vector< edge_descriptor > > out_edge_iterator; template< size_t ROWS, size_t COLS > std::pair< out_edge_iterator, out_edge_iterator > out_edges( vertex_descriptor vertex, graph< ROWS, COLS > const & g ) { boost::shared_ptr< std::vector< edge_descriptor > > edges( new std::vector< edge_descriptor >() ); if( g.cells[vertex] == FREE ) { size_t row = get_row( vertex, g ), col = get_col( vertex, g ); if( row != 0 ) { vertex_descriptor up = make_vertex( row - 1, col, g ); if( g.cells[up] == FREE ) edges->push_back( edge_descriptor( vertex, up ) ); } if( row != ROWS-1 ) { vertex_descriptor down = make_vertex( row + 1, col, g ); if( g.cells[down] == FREE ) edges->push_back( edge_descriptor( vertex, down ) ); } if( col != 0 ) { vertex_descriptor left = make_vertex( row, col - 1, g ); if( g.cells[left] == FREE ) edges->push_back( edge_descriptor( vertex, left ) ); } if( col != COLS-1 ) { vertex_descriptor right = make_vertex( row, col + 1, g ); if( g.cells[right] == FREE ) edges->push_back( edge_descriptor( vertex, right ) ); } } return boost::make_shared_container_range( edges ); } typedef size_t degree_size_type; template< size_t ROWS, size_t COLS > degree_size_type out_degree( vertex_descriptor vertex, graph< ROWS, COLS > const & g ) { std::pair< out_edge_iterator, out_edge_iterator > edges = out_edges( vertex, g ); return std::distance( edges.first, edges.second ); } }
到现在未知我们已经定义好了我们需要的boost::graph_traits
结构
namespace boost { template< size_t ROWS, size_t COLS > struct graph_traits< matrix::graph< ROWS, COLS > > { typedef matrix::vertex_descriptor vertex_descriptor; typedef matrix::edge_descriptor edge_descriptor; typedef matrix::out_edge_iterator out_edge_iterator; typedef matrix::vertex_iterator vertex_iterator; typedef boost::undirected_tag directed_category; typedef boost::disallow_parallel_edge_tag edge_parallel_category; struct traversal_category : virtual boost::vertex_list_graph_tag, virtual boost::incidence_graph_tag {}; typedef matrix::vertices_size_type vertices_size_type; typedef matrix::degree_size_type degree_size_type; static vertex_descriptor null_vertex() { return ROWS*COLS; } }; }
接下来就是如何展示使用BFS算法查找最短路径:
int main() { const size_t rows = 8, cols = 8; using namespace matrix; typedef graph< rows, cols > my_graph; my_graph g = { FREE, FREE, FREE, FREE, WALL, FREE, FREE, FREE, WALL, FREE, FREE, FREE, FREE, FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE, WALL, FREE, FREE, FREE, WALL, FREE, WALL, FREE, FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE, FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE, FREE, WALL, FREE, FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE, FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE, }; const vertex_descriptor start_vertex = make_vertex( 5, 1, g ), finish_vertex = make_vertex( 2, 6, g ); vertex_descriptor predecessors[rows*cols] = { 0 }; using namespace boost; breadth_first_search( g, start_vertex, visitor( make_bfs_visitor( record_predecessors( predecessors, on_tree_edge() ) ) ). vertex_index_map( identity_property_map() ) ); typedef std::list< vertex_descriptor > path; path p; for( vertex_descriptor vertex = finish_vertex; vertex != start_vertex; vertex = predecessors[vertex] ) p.push_front( vertex ); p.push_front( start_vertex ); for( path::const_iterator cell = p.begin(); cell != p.end(); ++cell ) std::cout << "[" << get_row( *cell, g ) << ", " << get_col( *cell, g ) << "]\n" ; return 0; }
以下是输出的从起点到终点的最短路径:
[5, 1] [4, 1] [4, 2] [3, 2] [2, 2] [1, 2] [1, 3] [1, 4] [1, 5] [1, 6] [2, 6]
源码如下:
#include <boost/graph/graph_traits.hpp> #include <boost/graph/breadth_first_search.hpp> #include <boost/graph/visitors.hpp> #include <boost/shared_container_iterator.hpp> #include <boost/shared_ptr.hpp> #include <vector> #include <boost/iterator/counting_iterator.hpp> #include <list> namespace matrix { typedef int cell; static const int FREE = 0; static const int WALL = 1; template< size_t ROWS, size_t COLS > struct graph { cell cells[ROWS*COLS]; }; typedef size_t vertex_descriptor; template< size_t ROWS, size_t COLS > size_t get_row( vertex_descriptor vertex, graph< ROWS, COLS > const & ) { return vertex / COLS; } template< size_t ROWS, size_t COLS > size_t get_col( vertex_descriptor vertex, graph< ROWS, COLS > const & ) { return vertex % COLS; } template< size_t ROWS, size_t COLS > vertex_descriptor make_vertex( size_t row, size_t col, graph< ROWS, COLS > const & ) { return row * COLS + col; } typedef const cell * vertex_iterator; template< size_t ROWS, size_t COLS > std::pair< vertex_iterator, vertex_iterator > vertices( graph< ROWS, COLS > const & g ) { return std::make_pair( g.cells, g.cells + ROWS*COLS ); } typedef size_t vertices_size_type; template< size_t ROWS, size_t COLS > vertices_size_type num_vertices( graph< ROWS, COLS > const & g ) { return ROWS*COLS; } typedef std::pair< vertex_descriptor, vertex_descriptor > edge_descriptor; bool operator==( edge_descriptor const & lhs, edge_descriptor const & rhs ) { return lhs.first == rhs.first && lhs.second == rhs.second || lhs.first == rhs.second && lhs.second == rhs.first; } bool operator!=( edge_descriptor const & lhs, edge_descriptor const & rhs ) { return !(lhs == rhs); } template< size_t ROWS, size_t COLS > vertex_descriptor source( edge_descriptor const & edge, graph< ROWS, COLS > const & ) { return edge.first; } template< size_t ROWS, size_t COLS > vertex_descriptor target( edge_descriptor const & edge, graph< ROWS, COLS > const & ) { return edge.second; } typedef boost::shared_container_iterator< std::vector< edge_descriptor > > out_edge_iterator; template< size_t ROWS, size_t COLS > std::pair< out_edge_iterator, out_edge_iterator > out_edges( vertex_descriptor vertex, graph< ROWS, COLS > const & g ) { boost::shared_ptr< std::vector< edge_descriptor > > edges( new std::vector< edge_descriptor >() ); if( g.cells[vertex] == FREE ) { size_t row = get_row( vertex, g ), col = get_col( vertex, g ); if( row != 0 ) { vertex_descriptor up = make_vertex( row - 1, col, g ); if( g.cells[up] == FREE ) edges->push_back( edge_descriptor( vertex, up ) ); } if( row != ROWS-1 ) { vertex_descriptor down = make_vertex( row + 1, col, g ); if( g.cells[down] == FREE ) edges->push_back( edge_descriptor( vertex, down ) ); } if( col != 0 ) { vertex_descriptor left = make_vertex( row, col - 1, g ); if( g.cells[left] == FREE ) edges->push_back( edge_descriptor( vertex, left ) ); } if( col != COLS-1 ) { vertex_descriptor right = make_vertex( row, col + 1, g ); if( g.cells[right] == FREE ) edges->push_back( edge_descriptor( vertex, right ) ); } } return boost::make_shared_container_range( edges ); } typedef size_t degree_size_type; template< size_t ROWS, size_t COLS > degree_size_type out_degree( vertex_descriptor vertex, graph< ROWS, COLS > const & g ) { std::pair< out_edge_iterator, out_edge_iterator > edges = out_edges( vertex, g ); return std::distance( edges.first, edges.second ); } } namespace boost { template< size_t ROWS, size_t COLS > struct graph_traits< matrix::graph< ROWS, COLS > > { typedef matrix::vertex_descriptor vertex_descriptor; typedef matrix::edge_descriptor edge_descriptor; typedef matrix::out_edge_iterator out_edge_iterator; typedef matrix::vertex_iterator vertex_iterator; typedef boost::undirected_tag directed_category; typedef boost::disallow_parallel_edge_tag edge_parallel_category; struct traversal_category : virtual boost::vertex_list_graph_tag, virtual boost::incidence_graph_tag {}; typedef matrix::vertices_size_type vertices_size_type; typedef matrix::degree_size_type degree_size_type; static vertex_descriptor null_vertex() { return ROWS*COLS; } }; } int main() { const size_t rows = 8, cols = 8; using namespace matrix; typedef matrix::graph< rows, cols > my_graph; my_graph g = { FREE, FREE, FREE, FREE, WALL, FREE, FREE, FREE, WALL, FREE, FREE, FREE, FREE, FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE, WALL, FREE, FREE, FREE, WALL, FREE, WALL, FREE, FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE, FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE, FREE, WALL, FREE, FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE, FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE, }; const vertex_descriptor start_vertex = make_vertex( 5, 1, g ), finish_vertex = make_vertex( 2, 6, g ); vertex_descriptor predecessors[rows*cols] = { 0 }; using namespace boost; breadth_first_search( g, start_vertex, visitor( make_bfs_visitor( record_predecessors( predecessors, on_tree_edge() ) ) ). vertex_index_map( boost::identity_property_map() ) ); typedef std::list< vertex_descriptor > path; path p; for( vertex_descriptor vertex = finish_vertex; vertex != start_vertex; vertex = predecessors[vertex] ) p.push_front( vertex ); p.push_front( start_vertex ); for( path::const_iterator cell = p.begin(); cell != p.end(); ++cell ) std::cout << "[" << get_row( *cell, g ) << ", " << get_col( *cell, g ) << "]\n" ; return 0; }
输出:
[5, 1] [4, 1] [4, 2] [3, 2] [2, 2] [1, 2] [1, 3] [1, 4] [1, 5] [1, 6] [2, 6]
怎样通过boost库的breadth_first_search算法查找点到点的最短路径