Pathfinding algorithm creating loops - c++

I've implemented the D*-Lite algorithm (here's a description, it's an algorithm for doing pathfinding when edge costs change over time), but I'm having problems with doing the edge cost updates. It works mostly, but sometimes it gets stuck in a loop, going back and forth between two vertices. I'm trying to create a test case which exhibits this behaviour, at the moment it happens for some cases when used in a large application, which makes it difficult to debug.
I'll get a test case up as soon as I can, but maybe someone can spot the error I've done going from pseudo to C++ right away.(There is a test case included below) The article presents an optimized version, Figure 4, which is the one I've implemented. The pseudocode is pasted below.
The source for my implementation is availible here.
If it helps, I'm using these types in my code:
struct VertexProperties { double x, y; };
typedef boost::adjacency_list<boost::vecS,
boost::vecS,
boost::undirectedS,
VertexProperties,
boost::property<boost::edge_weight_t, double> > Graph;
typedef boost::graph_traits<Graph>::vertex_descriptor Vertex;
typedef DStarEuclidianHeuristic<Graph, Vertex> Heuristic;
typedef DStarPathfinder<Graph, Heuristic> DStarPathfinder;
If any more information about usage is needed just ask, there's just too much to paste.
Pseudo code for D*-Lite:
procedure CalculateKey(s)
{01”} return [min(g(s), rhs(s)) + h(s_start, s) + km;min(g(s), rhs(s))];
procedure Initialize()
{02”} U = ∅;
{03”} km = 0;
{04”} for all s ∈ S rhs(s) = g(s) = ∞;
{05”} rhs(s_goal) = 0;
{06”} U.Insert(s_goal, [h(s_start, s_goal); 0]);
procedure UpdateVertex(u)
{07”} if (g(u) != rhs(u) AND u ∈ U) U.Update(u,CalculateKey(u));
{08”} else if (g(u) != rhs(u) AND u /∈ U) U.Insert(u,CalculateKey(u));
{09”} else if (g(u) = rhs(u) AND u ∈ U) U.Remove(u);
procedure ComputeShortestPath()
{10”} while (U.TopKey() < CalculateKey(s_start) OR rhs(s_start) > g(s_start))
{11”} u = U.Top();
{12”} k_old = U.TopKey();
{13”} k_new = CalculateKey(u));
{14”} if(k_old < k_new)
{15”} U.Update(u, k_new);
{16”} else if (g(u) > rhs(u))
{17”} g(u) = rhs(u);
{18”} U.Remove(u);
{19”} for all s ∈ Pred(u)
{20”} if (s != s_goal) rhs(s) = min(rhs(s), c(s, u) + g(u));
{21”} UpdateVertex(s);
{22”} else
{23”} g_old = g(u);
{24”} g(u) = ∞;
{25”} for all s ∈ Pred(u) ∪ {u}
{26”} if (rhs(s) = c(s, u) + g_old)
{27”} if (s != s_goal) rhs(s) = min s'∈Succ(s)(c(s, s') + g(s'));
{28”} UpdateVertex(s);
procedure Main()
{29”} s_last = s_start;
{30”} Initialize();
{31”} ComputeShortestPath();
{32”} while (s_start != s_goal)
{33”} /* if (g(s_start) = ∞) then there is no known path */
{34”} s_start = argmin s'∈Succ(s_start)(c(s_start, s') + g(s'));
{35”} Move to s_start;
{36”} Scan graph for changed edge costs;
{37”} if any edge costs changed
{38”} km = km + h(s_last, s_start);
{39”} s_last = s_start;
{40”} for all directed edges (u, v) with changed edge costs
{41”} c_old = c(u, v);
{42”} Update the edge cost c(u, v);
{43”} if (c_old > c(u, v))
{44”} if (u != s_goal) rhs(u) = min(rhs(u), c(u, v) + g(v));
{45”} else if (rhs(u) = c_old + g(v))
{46”} if (u != s_goal) rhs(u) = min s'∈Succ(u)(c(u, s') + g(s'));
{47”} UpdateVertex(u);
{48”} ComputeShortestPath()
EDIT:
I've succeeded in creating a test-case which shows the erronous behaviour. Running this along with the code in the pastebin, it will hang up in the last get_path call, going back and forth between nodes 1 and 2. It seems to me it is because the node 3 is never touched, and so going that way has an infinite cost.
#include <cmath>
#include <boost/graph/adjacency_list.hpp>
#include "dstar_search.h"
template <typename Graph, typename Vertex>
struct DStarEuclidianHeuristic {
DStarEuclidianHeuristic(const Graph& G_) : G(G_) {}
double operator()(const Vertex& u, const Vertex& v) {
double dx = G[u].x - G[v].x;
double dy = G[u].y - G[v].y;
double len = sqrt(dx*dx+dy*dy);
return len;
}
const Graph& G;
};
struct VertexProp {
double x, y;
};
int main() {
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
VertexProp, boost::property<boost::edge_weight_t, double> > Graph;
typedef boost::graph_traits<Graph>::vertex_descriptor Vertex;
typedef boost::graph_traits<Graph>::edge_descriptor Edge;
typedef DStarEuclidianHeuristic<Graph, Vertex> Heur;
typedef boost::property_map<Graph, boost::edge_weight_t>::type WMap;
Graph g(7);
WMap weights = boost::get(boost::edge_weight, g);
Edge e;
// Create a graph
e = boost::add_edge(0, 1, g).first;
weights[e] = sqrt(2.);
e = boost::add_edge(1, 2, g).first;
weights[e] = 1;
e = boost::add_edge(2, 3, g).first;
weights[e] = 1;
e = boost::add_edge(1, 4, g).first;
weights[e] = 1;
e = boost::add_edge(3, 4, g).first;
weights[e] = 1;
e = boost::add_edge(3, 5, g).first;
weights[e] = sqrt(2.);
e = boost::add_edge(2, 6, g).first;
weights[e] = sqrt(2.);
e = boost::add_edge(5, 6, g).first;
weights[e] = 1;
e = boost::add_edge(6, 7, g).first;
weights[e] = 1;
g[0].x = 1; g[0].y = 0;
g[1].x = 0; g[1].y = 1;
g[2].x = 0; g[2].y = 2;
g[3].x = 1; g[3].y = 2;
g[4].x = 1; g[4].y = 1;
g[5].x = 2; g[5].y = 3;
g[6].x = 1; g[6].y = 3;
g[7].x = 1; g[7].y = 4;
DStarPathfinder<Graph, Heur> dstar(g, Heur(g), 0, 7);
std::list<std::pair<Edge, double>> changes;
auto a = dstar.get_path(); // Find the initial path, works well
std::copy(a.begin(), a.end(), std::ostream_iterator<Vertex>(std::cout, ","));
// Now change the cost of going from 2->6, and try to find a new path
changes.push_back(std::make_pair(boost::edge(2, 6, g).first, 4.));
dstar.update(changes);
a = dstar.get_path(); // Stuck in loop
std::copy(a.begin(), a.end(), std::ostream_iterator<Vertex>(std::cout, ","));
return 0;
}
EDIT 2: More progress. If I replace the break condition in the while loop in ComputeShortestPath with just U != Ø (U is not empty), the path is found! It's quite slow though, since it always examines every node in the graph, which is not supposed to be neccessary. Also, since I use undirected graphs, I added some code to {40"} to update both u and v.

There are at least two problems with your code (not including the typenames I had to prepend to constructs like std::vector<TemplateParameter>::iterator in order to compile it).
You're using a non-admissible heuristic, since the diagonals cost 1 but have length √2. This prevents the second call to ComputeShortestPath from doing anything at all.
The update method of the heap you're using (which is private by convention to Boost and thus apparently undocumented) supports key decreases only. D* Lite needs key increases as well.

Unfortunately, posting pseudocode isn't really useful here since the pseudocode may be correct but the actual implementation may be at fault.
Generally, in path finding algorithms, if you're cycling between nodes then there is a really good chance that the algorithm is not removing visited nodes from the set of potential route nodes. This is usually done by setting a flag on the node as you traverse through it and reset the flag as you back step up the search tree.

The problem is in the UpdateVertex function.
The psuedo-code was written assuming that the comparisons are on integers (which they are in the papers). In your implementation you're doing comparisons on floating point values. You need to add a tolerance if you are dealing with non-integer costs.
You can test this on GCC by compiling with -Wfloat-equal (or even better -Werror=float-equal)

I'm having your same problem too. I think I got the cause, maybe in this time you find the solution to your problem and can give me some tips.
I think the problem come from the U list.
Since probably some key of every vertex have a value higher than the key of the s_start.
So ComputeKey(s)<ComputeKeu(s_start) is not satisfied (first condition of the while in ComputePath), the second condition rhs(s_start)>g(s_start) is not satisfied since when you move along the path you move through cells that are being made consistent.
Then when this two conditions don't hold the while stop, so the program stops expanding new cells.
When you go calculating the path, using as successive along the path the one that minimize g(s)+c(u,s) you end up on a cell the still has and infinite g cost (cause it has not been expanded in the while cycle).
This is the reason even to the fact that if you change condition, using while U!=0 the algorithm works, this force the program to expand all the vertices in the U list. (But you definitely lost the advantages of dynamical algorithm).
Now I hope I've helped you, if you don't need this help no more maybe you can help me.

I had this issue myself when implementing both the D* Lite (regular version) and the optimized version. I am a bit unsure why it happens in the first place, but I seems to be triggered for some arrangement of obstacles (cross shaped or tall vertical obstacles) where the algorithm suddenly is unable to explore more options and end up jumping back and forth between two options in an infinite loop. I already created a post on this earlier here and how I bypassed the infinite loop issue, however at the cost of the algorithm probably becoming a little slower.

Related

Boost Graph Library: Deleting an edge while using an iterator

Currently I am trying to implement a Girvan Newman Algorithm for a class project using boost graph library in c++.
At the moment, edges and vertices are being added fine, but I am having an issue removing edges. I am using an iterator to add my edges, and am now trying to remove them. Everything functions as expected until I try to remove an edge, and then I hit a segfault. I have tried removing the edge using the conditional remove_edge_if() and tried some of the other boost options - still having the same issue.
enum {A, B, C, D, E};
num_vertices = 5;
//writing out edges in graph
typedef std::pair<int, int> Edge;
Edge edge_array[] = {
Edge(A,B),
Edge(A,D),
Edge(C,A),
Edge(D,C),
Edge(C,E),
Edge(D,E)
};
const int num_edges = sizeof(edge_array)/sizeof(edge_array[0]);
//declare a graph object
Graph g(num_vertices);
//add the edges to the graph object
for(int i=0;i<num_edges;i++){
add_edge(edge_array[i].first, edge_array[i].second, g);
}
//actual algorithm starts here
//get property map for vertex indices
index = get(boost::vertex_index, g);
//calculate all centrality values and store in vector betweenness
calcCentrality();
//set up iterators to run through all edges
boost::graph_traits<Graph>::edge_iterator ei, ei_end;
boost::graph_traits<Graph>::edge_iterator loc;
std::tie(ei, ei_end)=edges(g);
for(int i=0;i<num_edges;i++){
std::cout << "(" << index[source(*ei, g)] << "," << index[target(*ei, g)] << ") ";
ei++;
}
std::cout << std::endl;
std::tie(ei, ei_end)=edges(g);
int max_B = 0;
boost::graph_traits<Graph>::vertex_descriptor max_B_edge1, max_B_edge2;
//iterate through all edges-1
for(int i=0;i<num_edges-1;i++){
//check betweenness centrality of edge i
int BC = betweeness[i];
if(BC > max_B){
max_B = BC;
max_B_edge1 = source(*ei, g);
max_B_edge2 = target(*ei, g);
}
ei++;
}
boost::remove_edge(max_B_edge1, max_B_edge2, g);
std::cout << std::endl;
std::tie(ei, ei_end)=edges(g);
for(int i=0;i<num_edges;i++){
std::cout << "(" << index[source(*ei, g)] << "," << index[target(*ei, g)] << ") ";
ei++;
}
std::cout << std::endl;
}
Any guidance on how to resolve this, or a new approach would be extremely helpful. I think the issue is with my iterator
The bigger problem is that you are indexing betweenness by an integer [0, num_edges). This doesn't make a lot of sense unless betweenness is actually an edge centrality map (not the vertex centrality map, or just "centrality map").
But that is making things weirder, because that would suggest an associative container like map<edge_descriptor, double> instead of a vector<>, because edge_descriptor is not an integral type.
Of course, one can paper over this by creating an explicit edge_index property/map and projecting through that to a integral-indexed container, e.g.
std::vector<double> betweenness(num_edges(g));
auto edge_id_map = get(boost::edge_index, g); // or assumed externally supplied?
auto edge_centrality_map = boost::make_safe_iterator_property_map(
betweenness.begin(),
betweenness.size(),
edge_id_map);
Now, if that were the case, one has to wonder why the same map is not used to access the betweenness by descriptor... instead of implicitly recalculating an edge index again in a loop-variable (int i). That is unsafe especially since you're modifying the edges, so re-running the loop will calculate different i for each edge! Oops.
Instead, I'd make betweenness associative, indexed by edge-descriptor. Of course, I can't show the relevant changes, because calcCentrality is missing. Even betweenness is missing (which by the way suggests that it must be a global variable? Not a very robust idea if so).
Let's assume for a moment that you have edge_centrality_map defined as above, or, in my preferred approach, like this:
// more stable idea:
std::map<Graph::edge_descriptor, double> betweenness;
auto edge_centrality_map = boost::make_assoc_property_map(betweenness);
Now the whole... mess with the loop could be replace with:
std::map<Graph::edge_descriptor, double> betweenness;
auto edge_centrality_map = boost::make_assoc_property_map(betweenness);
while (num_edges(g)) {
auto ee = edges(g);
auto [min, max] = std::minmax_element(
ee.first, ee.second, [ecm = edge_centrality_map](auto a, auto b) {
return ecm[a] < ecm[b];
});
remove_edge(*max, g);
print_edges();
}
For good measure I used minmax_element to show off the convenience of using standard algorithms here. We use the lambda to "simply" project through the centrality map. No manual juggling with loop variables and translating to vector indexes. This is less work, but more importantly less room for error.
Here's the code presented modernized and showing the suggested approach:
Live On Compiler Explorer
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/betweenness_centrality.hpp>
#include <iostream>
using Graph = boost::adjacency_list<>;
int main() {
enum { A, B, C, D, E, N /*==5*/ };
Graph g(N);
auto print_edges = [&g] { // helper to avoid repeating code
for (auto e : boost::make_iterator_range(edges(g)))
std::cout << e << " ";
std::cout << std::endl;
};
// add the edges to the graph object
for (auto [s, t] :
std::array{std::pair{A, B}, std::pair{A, D}, std::pair{C, A},
std::pair{D, C}, std::pair{C, E}, std::pair{D, E}}) //
{
add_edge(s, t, g);
}
print_edges();
// actual algorithm starts here
// get property map for vertex indices
auto index = get(boost::vertex_index, g);
// calculate all centrality values and store in vector betweenness
// calcCentrality();
//std::vector<int> betweenness(num_edges(g)); // JUST GUESSING
// more stable idea:
std::map<Graph::edge_descriptor, double> betweenness;
auto edge_centrality_map = boost::make_assoc_property_map(betweenness);
while (num_edges(g)) {
auto ee = edges(g);
auto [min, max] = std::minmax_element(
ee.first, ee.second, [ecm = edge_centrality_map](auto a, auto b) {
return ecm[a] < ecm[b];
});
boost::remove_edge(*max, g);
print_edges();
}
}
Prints dummy output (because the betweenness is defaulted to 0.0 for each edge):
(0,1) (0,3) (2,0) (2,4) (3,2) (3,4)
(0,1) (0,3) (2,0) (2,4) (3,2)
(0,1) (0,3) (2,0) (2,4)
(0,1) (0,3) (2,0)
(0,1) (0,3)
(0,1)
BONUS
In fact, the loop is not very optimal, because instead of repeatedly finding the maximum element of a container, just to remove them can be thought of as sorting, and then removing edges in order.
Live On Compiler Explorer
auto [e_begin, e_end] = edges(g);
std::vector ascending(e_begin, e_end);
std::stable_sort(begin(ascending), end(ascending), edge_compare);
while (not ascending.empty()) {
remove_edge(ascending.back(), g);
ascending.pop_back();
print_edges();
}
Which gives the same output, just much quicker.
Also note that if your chosen edge_centrality_map suffers from
index/iterator/descriptor or reference invalidation in some way on the
remove_edge operation, this is safer, because it only depends on
the stability of edge descriptors in your chosen Graph model.
In fact, you might not even need/want to remove the edges "physically" to save time and work. After all, you know which edges to leave out when printing.

Optimize Network Graph creation

I have the following code that goes through a matrix of 188k x 188k rows of data and attempts to create a network graph out of it. The problem here is my algorithm is extremely slow (as expected since its quadratic). Is there a better way of doing this that I'm not seeing? I'm already thinking of using openMP to parallelize this but would be great if I don't have to.
Here's whats true about my matrix - its symmetric, its over 188 thousand by 188 thousand, each value in the matrix corresponds to the edge weight So for example, an element aij is the weight of the edge between i and j. Here's my code:
The graph creation:
typedef boost::adjacency_list
<
boost::vecS,
boost::vecS,
boost::undirectedS,
boost::property<boost::vertex_name_t, std::string>,
boost::property<boost::edge_weight_t, float>,
boost::property<boost::graph_name_t, std::string>
> UGraph;
typedef UGraph::vertex_descriptor vertex_t;
typedef UGraph::edge_descriptor edge_t;
Now the function creating the network:
vertex_t u;
vertex_t v;
edge_t e;
bool found=0;
int idx =0;
float cos_similarity;
for(int p =1;p<=adj_matrix.cols();p++){
//using a previously created vector to track already created nodes
if(std::find(created_nodes.begin(), created_nodes.end(), nodes[idx]) == created_nodes.end()){
u = add_vertex(nodes[idx], ug);
created_nodes.push_back(nodes[idx]);
}else{
u = vertex(p,ug);
}
int jdx = 0;
for(int q =1;q<=adj_matrix.cols();q++){
if(p!=q){//NO LOOPS IN THIS GRAPH
//using a previously created vector to track already created nodes
if(std::find(created_nodes.begin(), created_nodes.end(), nodes[jdx]) == created_nodes.end()){
v = add_vertex(nodes[jdx], ug);
created_nodes.push_back(nodes[jdx]);
}else{
u = vertex(q,ug);
}
tie(e, found) = edge(u, v, ug);
if(!found){//check that edge does not already exist
cos_similarity = adj_matrix(p,q);
fil<<cos_similarity<<endl;
fil.flush();
if(cos_similarity >= 0.2609){ //only add edge if value of cell is greater than this threshold
boost::add_edge(u,v,cos_similarity, ug);
edge_out<<p<<" "<<q<<" "<<cos_similarity<<endl; //creating an edge-weight list for later use
}
}
}
jdx++;
}
idx++;
}
A simple tip:
I think your algorithm is cubic rather than quadratic, because vector and std::find(vector.begin(), vector.end()) are used to avoid duplicates in the inside loop.
To avoid duplicates and keep the algorithm quadraic, you can just traverse the upper triangle of the matrix as it's symmetric, which means the graph is an undirected graph.

Optimal way to find shared elements between combination pairs

I have a list of ordered items of type A, who each contain a subset from a list of items B. For each pair of items in A, I would like to find the number of items B that they share (intersect).
For example, if I have this data:
A1 : B1
A2 : B1 B2 B3
A3 : B1
Then I would get the following result:
A1, A2 : 1
A1, A3 : 1
A2, A3 : 1
The problem I'm having is making the algorithm efficient. The size of my dataset is about 8.4K items of type A. This means 8.4K choose 2 = 35275800 combinations. The algorithm I'm using is simply going through each combination pair and doing a set intersection.
The gist of what I have so far is below. I am storing the counts as a key in a map, with the value as a vector of A pairs. I'm using a graph data structure to store the data, but the only 'graph' operation I'm using is get_neighbors() which returns the B subset for an item from A. I happen to know that the elements in the graph are ordered from index 0 to 8.4K.
void get_overlap(Graph& g, map<int, vector<A_pair> >& overlap) {
map<int, vector<A_pair> >::iterator it;
EdgeList el_i, el_j;
set<int> intersect;
size_t i, j;
VertexList vl = g.vertices();
for (i = 0; i < vl.size()-1; i++) {
el_i = g.get_neighbors(i);
for (j = i+1; j < vl.size(); j++) {
el_j = g.get_neighbors(j);
set_intersection(el_i.begin(), el_i.end(), el_j.begin(), el_j.end(), inserter(intersect, intersect.begin()));
int num_overlap = intersect.size();
it = overlap.find(num_overlap);
if (it == overlap.end()) {
vector<A_pair> temp;
temp.push_back(A_pair(i, j));
overlap.insert(pair<int, vector<A_pair> >(num_overlap, temp));
}
else {
vector<A_pair> temp = it->second;
temp.push_back(A_pair(i, j));
overlap[num_overlap] = temp;
}
}
}
}
I have been running this program for nearly 24 hours, and the ith element in the for loop has reached iteration 250 (I'm printing each i to a log file). This, of course, is a long way from 8.4K (although I know as iterations go on, the number of comparisons will shorten since j = i +1). Is there a more optimal approach?
Edit: To be clear, the goal here is ultimately to find the top k overlapped pairs.
Edit 2: Thanks to #Beta and others for pointing out optimizations. In particular, updating the map directly (instead of copying its contents and resetting the map value) drastically improved the performance. It now runs in a matter of seconds.
I think you may be able to make things faster by pre-computing a reverse (edge-to-vertex) map. This would allow you to avoid the set_intersection call, which performs a bunch of costly set insertions. I am missing some declarations to make fully functional code, but hopefully you will get the idea. I am assuming that EdgeList is some sort of int vector:
void get_overlap(Graph& g, map<int, vector<A_pair> >& overlap) {
map<int, vector<A_pair> >::iterator it;
EdgeList el_i, el_j;
set<int> intersect;
size_t i, j;
VertexList vl = g.vertices();
// compute reverse map
map<int, set<int>> reverseMap;
for (i = 0; i < vl.size()-1; i++) {
el_i = g.get_neighbors(i);
for (auto e : el_i) {
const auto findIt = reverseMap.find(e);
if (end(reverseMap) == findIt) {
reverseMap.emplace(e, set<int>({i})));
} else {
findIt->second.insert(i);
}
}
}
for (i = 0; i < vl.size()-1; i++) {
el_i = g.get_neighbors(i);
for (j = i+1; j < vl.size(); j++) {
el_j = g.get_neighbors(j);
int num_overlap = 0;
for (auto e: el_i) {
auto findIt = reverseMap.find(e);
if (end(reverseMap) != findIt) {
if (findIt->second.count(j) > 0) {
++num_overlap;
}
}
}
it = overlap.find(num_overlap);
if (it == overlap.end()) {
overlap.emplace(num_overlap, vector<A_pair>({ A_pair(i, j) }));
}
else {
it->second.push_back(A_pair(i,j));
}
}
}
I didn't do the precise performance analysis, but inside the double loop, you replace "At most 4N comparisons" + some costly set insertions (from set_intersection) with N*log(M)*log(E) comparisons, where N is the average number of edge per vertex, and M is the average number of vertex per edge, and E is the number of edges, so it could be beneficial depending on your data set.
Also, if your edge indexes are compact, then you can use a simplae vector rather than a map to represent the reverse map, which removed the log(E) performance cost.
One question, though. Since you're talking about vertices and edges, don't you have the additional constraint that edges always have 2 vertices ? This could simplify some computations.

shortest path algorithm from text input

I've been trying to do this shortest path problem and I realised that the way I was trying to it was almost completely wrong and that I have no idea to complete it.
The question requires you to find the shortest path from one point to another given a text file of input.
The input looks like this with the first value representing how many levels there are.
4
14 10 15
13 5 22
13 7 11
5
This would result in an answer of: 14+5+13+11+5=48
The question asks for the shortest path from the bottom left to the top right.
The way I have attempted to do this is to compare the values of either path possible and then add them to a sum. e.g the first step from the input I provided would compare 14 against 10 + 15. I ran into the problem that if both values are the same it will stuff up the rest of the working.
I hope this makes some sense.
Any suggestions on an algorithm to use or any sample code would be greatly appreciated.
Assume your data file is read into a 2D array of the form:
int weights[3][HEIGHT] = {
{14, 10, 15},
{13, 5, 22},
{13, 7, 11},
{X, 5, X}
};
where X can be anything, doesn't matter. For this I'm assuming positive weights and therefore there is never a need to consider a path that goes "down" a level.
In general you can say that the minimum cost is lesser of the following 2 costs:
1) The cost of rising a level: The cost of the path to the opposite side from 1 level below, plus the cost of coming up.
2) The cost of moving across a level : The cost of the path to the opposite from the same level, plus the cost of coming across.
int MinimumCost(int weight[3][HEIGHT]) {
int MinCosts[2][HEIGHT]; // MinCosts[0][Level] stores the minimum cost of reaching
// the left node of that level
// MinCosts[1][Level] stores the minimum cost of reaching
// the right node of that level
MinCosts[0][0] = 0; // cost nothing to get to the start
MinCosts[0][1] = weight[0][1]; // the cost of moving across the bottom
for (int level = 1; level < HEIGHT; level++) {
// cost of coming to left from below right
int LeftCostOneStep = MinCosts[1][level - 1] + weight[2][level - 1];
// cost of coming to left from below left then across
int LeftCostTwoStep = MinCosts[0][level - 1] + weight[0][level - 1] + weight[1][level];
MinCosts[0][level] = Min(LeftCostOneStep, LeftCostTwoStep);
// cost of coming to right from below left
int RightCostOneStep = MinCosts[0][level - 1] + weight[0][level - 1];
// cost of coming to right from below right then across
int RightCostTwoStep = MinCosts[1][level - 1] + weight[1][level - 1] + weight[1][level];
MinCosts[1][level] = Min(RightCostOneStep, RightCostTwoStep);
}
return MinCosts[1][HEIGHT - 1];
}
I haven't double checked the syntax, please only use it to get a general idea of how to solve the problem. You could also rewrite the algorithm so that MinCosts uses constant memory, MinCosts[2][2] and your whole algorithm could become a state machine.
You could also use dijkstra's algorithm to solve this, but that's a bit like killing a fly with a nuclear warhead.
My first idea was to represent the graph with a matrix and then run a DFS or Dijkstra to solve it. But for this given question, we can do better.
So, here is a possible solution of this problem that runs in O(n). 2*i means left node of level i and 2*i+1 means right node of level i. Read the comments in this solution for an explanation.
#include <stdio.h>
struct node {
int lup; // Cost to go to level up
int stay; // Cost to stay at this level
int dist; // Dist to top right node
};
int main() {
int N;
scanf("%d", &N);
struct node tab[2*N];
// Read input.
int i;
for (i = 0; i < N-1; i++) {
int v1, v2, v3;
scanf("%d %d %d", &v1, &v2, &v3);
tab[2*i].lup = v1;
tab[2*i].stay = tab[2*i+1].stay = v2;
tab[2*i+1].lup = v3;
}
int v;
scanf("%d", &v);
tab[2*i].stay = tab[2*i+1].stay = v;
// Now the solution:
// The last level is obvious:
tab[2*i+1].dist = 0;
tab[2*i].dist = v;
// Now, for each level, we compute the cost.
for (i = N - 2; i >= 0; i--) {
tab[2*i].dist = tab[2*i+3].dist + tab[2*i].lup;
tab[2*i+1].dist = tab[2*i+2].dist + tab[2*i+1].lup;
// Can we do better by staying at the same level ?
if (tab[2*i].dist > tab[2*i+1].dist + tab[2*i].stay) {
tab[2*i].dist = tab[2*i+1].dist + tab[2*i].stay;
}
if (tab[2*i+1].dist > tab[2*i].dist + tab[2*i+1].stay) {
tab[2*i+1].dist = tab[2*i].dist + tab[2*i+1].stay;
}
}
// Print result
printf("%d\n", tab[0].dist);
return 0;
}
(This code has been tested on the given example.)
Use a depth-first search and add only the minimum values. Then check which side is the shortest stair. If it's a graph problem look into a directed graph. For each stair you need 2 vertices. The cost from ladder to ladder can be something else.
The idea of a simple version of the algorithm is the following:
define a list of vertices (places where you can stay) and edges (walks you can do)
every vertex will have a list of edges connecting it to other vertices
for every edge store the walk length
for every vertex store a field with 1000000000 with the meaning "how long is the walk to here"
create a list of "active" vertices initialized with just the starting point
set the walk-distance field of starting vertex with 0 (you're here)
Now the search algorithm proceeds as
pick the (a) vertex from the "active list" with lowest walk_distance and remove it from the list
if the vertex is the destination you're done.
otherwise for each edge in that vertex compute the walk distance to the other_vertex as
new_dist = vertex.walk_distance + edge.length
check if the new distance is shorter than other_vertex.walk_distance and in this case update other_vertex.walk_distance to the new value and put that vertex in the "active list" if it's not already there.
repeat from 1
If you run out of nodes in the active list and never processed the destination vertex it means that there was no way to reach the destination vertex from the starting vertex.
For the data structure in C++ I'd use something like
struct Vertex {
double walk_distance;
std::vector<struct Edge *> edges;
...
};
struct Edge {
double length;
Vertex *a, *b;
...
void connect(Vertex *va, Vertex *vb) {
a = va; b = vb;
va->push_back(this); vb->push_back(this);
}
...
};
Then from the input I'd know that for n levels there are 2*n vertices needed (left and right side of each floor) and 2*(n-1) + n edges needed (one per each stair and one for each floor walk).
For each floor except the last you need to build three edges, for last floor only one.
I'd also allocate all edges and vertices in vectors first, fixing the pointers later (post-construction setup is an anti-pattern but here is to avoid problems with reallocations and still maintaining things very simple).
int n = number_of_levels;
std::vector<Vertex> vertices(2*n);
std::vector<Edge> edges(2*(n-1) + n);
for (int i=0; i<n-1; i++) {
Vertex& left = &vertices[i*2];
Vertex& right = &vertices[i*2 + 1];
Vertex& next_left = &vertices[(i+1)*2];
Vertex& next_right = &vertices[(i+1)*2 + 1];
Edge& dl_ur = &edges[i*3]; // down-left to up-right stair
Edge& dr_ul = &edges[i*3+1]; // down-right to up-left stair
Edge& floor = &edges[i*3+2];
dl_ur.connect(left, next_right);
dr_ul.connect(right, next_left);
floor.connect(left, right);
}
// Last floor
edges.back().connect(&vertex[2*n-2], &vertex[2*n-1]);
NOTE: untested code
EDIT
Of course this algorithm can solve a much more general problem where the set of vertices and edges is arbitrary (but lengths are non-negative).
For the very specific problem a much simpler algorithm is possible, that doesn't even need any data structure and that can instead compute the result on the fly while reading the input.
#include <iostream>
#include <algorithm>
int main(int argc, const char *argv[]) {
int n; std::cin >> n;
int l=0, r=1000000000;
while (--n > 0) {
int a, b, c; std::cin >> a >> b >> c;
int L = std::min(r+c, l+b+c);
int R = std::min(r+b+a, l+a);
l=L; r=R;
}
int b; std::cin >> b;
std::cout << std::min(r, l+b) << std::endl;
return 0;
}
The idea of this solution is quite simple:
l variable is the walk_distance for the left side of the floor
r variable is the walk_distance for the right side
Algorithm:
we initialize l=0 and r=1000000000 as we're on the left side
for all intermediate steps we read the three distances:
a is the length of the down-left to up-right stair
b is the length of the floor
c is the length of the down-right to up-left stair
we compute the walk_distance for left and right side of next floor
L is the minimum between r+c and l+b+c (either we go up starting from right side, or we go there first starting from left side)
R is the minimum betwen l+a and r+b+a (either we go up starting from left, or we start from right and cross the floor first)
for the last step we just need to chose what is the minimum between r and coming there from l by crossing the last floor

Optimizing a dijkstra implementation

QUESTION EDITED, now I only want to know if a queue can be used to improve the algorithm.
I have found this implementation of a mix cost max flow algorithm, which uses dijkstra: http://www.stanford.edu/~liszt90/acm/notebook.html#file2
Gonna paste it here in case it gets lost in the internet void:
// Implementation of min cost max flow algorithm using adjacency
// matrix (Edmonds and Karp 1972). This implementation keeps track of
// forward and reverse edges separately (so you can set cap[i][j] !=
// cap[j][i]). For a regular max flow, set all edge costs to 0.
//
// Running time, O(|V|^2) cost per augmentation
// max flow: O(|V|^3) augmentations
// min cost max flow: O(|V|^4 * MAX_EDGE_COST) augmentations
//
// INPUT:
// - graph, constructed using AddEdge()
// - source
// - sink
//
// OUTPUT:
// - (maximum flow value, minimum cost value)
// - To obtain the actual flow, look at positive values only.
#include <cmath>
#include <vector>
#include <iostream>
using namespace std;
typedef vector<int> VI;
typedef vector<VI> VVI;
typedef long long L;
typedef vector<L> VL;
typedef vector<VL> VVL;
typedef pair<int, int> PII;
typedef vector<PII> VPII;
const L INF = numeric_limits<L>::max() / 4;
struct MinCostMaxFlow {
int N;
VVL cap, flow, cost;
VI found;
VL dist, pi, width;
VPII dad;
MinCostMaxFlow(int N) :
N(N), cap(N, VL(N)), flow(N, VL(N)), cost(N, VL(N)),
found(N), dist(N), pi(N), width(N), dad(N) {}
void AddEdge(int from, int to, L cap, L cost) {
this->cap[from][to] = cap;
this->cost[from][to] = cost;
}
void Relax(int s, int k, L cap, L cost, int dir) {
L val = dist[s] + pi[s] - pi[k] + cost;
if (cap && val < dist[k]) {
dist[k] = val;
dad[k] = make_pair(s, dir);
width[k] = min(cap, width[s]);
}
}
L Dijkstra(int s, int t) {
fill(found.begin(), found.end(), false);
fill(dist.begin(), dist.end(), INF);
fill(width.begin(), width.end(), 0);
dist[s] = 0;
width[s] = INF;
while (s != -1) {
int best = -1;
found[s] = true;
for (int k = 0; k < N; k++) {
if (found[k]) continue;
Relax(s, k, cap[s][k] - flow[s][k], cost[s][k], 1);
Relax(s, k, flow[k][s], -cost[k][s], -1);
if (best == -1 || dist[k] < dist[best]) best = k;
}
s = best;
}
for (int k = 0; k < N; k++)
pi[k] = min(pi[k] + dist[k], INF);
return width[t];
}
pair<L, L> GetMaxFlow(int s, int t) {
L totflow = 0, totcost = 0;
while (L amt = Dijkstra(s, t)) {
totflow += amt;
for (int x = t; x != s; x = dad[x].first) {
if (dad[x].second == 1) {
flow[dad[x].first][x] += amt;
totcost += amt * cost[dad[x].first][x];
} else {
flow[x][dad[x].first] -= amt;
totcost -= amt * cost[x][dad[x].first];
}
}
}
return make_pair(totflow, totcost);
}
};
My question is if it can be improved by using a priority queue inside of Dijkstra(). I tried but I couldn't get it to work properly.
Actually I suspect that in Dijkstra it should be looping over adjacent nodes, not all nodes...
Thanks a lot.
Surely Dijkstra's algorithm can be improved by using minheap. After we put a vertex into shortest-path tree and process (i.e. label) all adjacent vertices, our next step is to select the vertex with smallest label, not yet in the tree.
This is where minheap comes to mind. Rather than sequentially scan through all vertices, we extract the min element from heap and restructure it, which takes O(logn) time vs O(n). Note that the heap is going to keep only those vertices that are not yet in the shortest-path tree. However we should be able to somehow modify vertices in the heap, if we update their labels.
I am not so sure using a priority queue to implement Dijkstra's algorithm will actually improve the run time because, while using a priority queue decreases the amount of time needed to find the vertex with minimum distance from the source (O(log V) with a priority queue vs. O(V) in the naive implementation), it also increases the amount of time needed to process a new edge (O(log V) with a priority queue vs. O(1) in the naive implementation).
Thus, for the naive implementation, the running time is O(V^2+E).
However, for the priority queue implementation, the running time is O(V log V+E log V).
For very dense graphs, E could be O(V^2), which means the naive implementation would have running time O(V^2+V^2)=O(V^2) while the priority queue implementation would have running time O(V log V+V^2 log V)=O(V^2 log V). Thus, as you can see, the priority queue implementation actually has a worse worst-case run time in the case of dense graphs.
Given the fact that the people writing the above implementation stored the edges as an adjacency matrix rather than using adjacency lists, it looks like the people who wrote this code were expecting the graph to be a dense graph with O(V^2) edges, so it makes sense that they would use the naive implementation over the priority queue implementation here.
For more info about running time of Dijkstra's algorithm, read up on this Wikipedia page.