(Извиняюсь заранее за длину этого ответа. Это было время, так как я использовал BGL, и я думал, что это будет сделать хороший освежитель. Полный код here.)
Красота из Boost Graph Library (и общее программирование в целом) заключается в том, что вам не нужно использовать какую-либо конкретную структуру данных, чтобы воспользоваться данным алгоритмом. Матрица, которую вы предоставили вместе с правилами об обходе, уже определяет график. Все, что необходимо, - это кодирование этих правил в классе признаков, который может использоваться для использования алгоритмов BGL.
В частности, мы хотим определить специализацию boost::graph_traits<T>
для вашего графика. Предположим, что ваша матрица представляет собой один массив из int
в формате строки. К сожалению, специализация graph_traits
для int[N]
будет недостаточной, так как она не предоставляет никакой информации о размерах матрицы. Итак, давайте определим ваш график следующим образом:
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
. Но сначала давайте определим некоторые из функций и типов, которые нам понадобятся.
типа и вспомогательные функции Vertex:
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; }
};
}
А вот как выполнить поиск в ширину и найти кратчайшие пути:
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]
Вот код работает на [ideone] (http://ideone.com/UOnXZ). Я должен был сделать одно или два изменения, чтобы заставить его компилироваться без предупреждения. –
@AaronMcDaid Спасибо! Я не понимал, что на идеоне есть импульс. –
Спасибо! Это было очень полезно! –