203 lines
6.5 KiB
C++
203 lines
6.5 KiB
C++
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <queue>
|
|
#include <map>
|
|
using namespace std;
|
|
|
|
/**************************************************************************
|
|
* The Transition class holds a (cost, destination node) pair *
|
|
*************************************************************************/
|
|
class Transition
|
|
{
|
|
public:
|
|
int cost;
|
|
string destination;
|
|
};
|
|
|
|
/**************************************************************************
|
|
* Path objects represent a path through the the graph starting at the *
|
|
* start node. The Path object has a certain cost associated with it. *
|
|
* The path may or may not end on the goal node. *
|
|
*************************************************************************/
|
|
class Path
|
|
{
|
|
public:
|
|
int cost;
|
|
vector<string> node_list;
|
|
map<string, bool> visited_nodes;
|
|
|
|
Path()
|
|
{
|
|
cost = 0;
|
|
}
|
|
Path(const Path & orig, const Transition & t)
|
|
{
|
|
cost = orig.cost + t.cost;
|
|
node_list = orig.node_list;
|
|
node_list.push_back(t.destination);
|
|
visited_nodes = orig.visited_nodes;
|
|
visited_nodes[t.destination] = true;
|
|
}
|
|
|
|
// comparison operator for making a min priority_queue
|
|
bool operator()(const Path & p1, const Path & p2)
|
|
{
|
|
return p1.cost > p2.cost;
|
|
}
|
|
};
|
|
/* This function allows Path objects to be printed to ostream objects */
|
|
ostream & operator<<(ostream & out, const Path & p)
|
|
{
|
|
int sz = p.node_list.size();
|
|
out << '[';
|
|
for (int i = 0; i < sz; i++)
|
|
{
|
|
out << p.node_list[i];
|
|
if (i < sz-1)
|
|
out << ", ";
|
|
}
|
|
out << ']';
|
|
return out;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* Global type definitions *
|
|
*************************************************************************/
|
|
typedef vector<Transition> Transition_List_t;
|
|
typedef map<string, Transition_List_t> Transition_Table_t;
|
|
|
|
/**************************************************************************
|
|
* Function Prototypes *
|
|
*************************************************************************/
|
|
void createTransition(Transition_Table_t & transitions,
|
|
string src, string dest, int cost);
|
|
void solveMinEnergy(string start, string goal,
|
|
Transition_Table_t & transitions);
|
|
|
|
/**************************************************************************
|
|
* Main Function *
|
|
*************************************************************************/
|
|
int main(int argc, char * argv[])
|
|
{
|
|
if (argc != 2)
|
|
{
|
|
cerr << "Usage: minimum-energy <input-file>" << endl;
|
|
return 42;
|
|
}
|
|
|
|
ifstream i(argv[1]);
|
|
if (!i.is_open())
|
|
{
|
|
cerr << "Could not open " << argv[1] << endl;
|
|
return 1;
|
|
}
|
|
|
|
string startNode;
|
|
string goalNode;
|
|
Transition_Table_t node_transitions;
|
|
|
|
// Read the input file
|
|
i >> startNode;
|
|
i >> goalNode;
|
|
while (!i.eof())
|
|
{
|
|
string src, dest;
|
|
int cost;
|
|
i >> src >> dest >> cost;
|
|
createTransition(node_transitions, src, dest, cost);
|
|
}
|
|
i.close();
|
|
|
|
solveMinEnergy(startNode, goalNode, node_transitions);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* Add a new transition into the node_transitions table *
|
|
*************************************************************************/
|
|
void createTransition(Transition_Table_t & transitions,
|
|
string src, string dest, int cost)
|
|
{
|
|
if (transitions.find(src) == transitions.end())
|
|
{
|
|
/* src is not a key in transitions, so insert a new one */
|
|
Transition_List_t tl;
|
|
transitions[src] = tl;
|
|
}
|
|
Transition t;
|
|
t.cost = cost;
|
|
t.destination = dest;
|
|
transitions[src].push_back(t);
|
|
}
|
|
|
|
/**************************************************************************
|
|
* Solve the minimum energy problem for start node start, goal node goal, *
|
|
* and transition table set transitions *
|
|
*************************************************************************/
|
|
void solveMinEnergy(string start, string goal,
|
|
Transition_Table_t & transitions)
|
|
{
|
|
priority_queue<Path, vector<Path>, Path> pathQueue;
|
|
/* Create an initial path with just the starting node in it */
|
|
Path init;
|
|
init.cost = 0;
|
|
init.node_list.push_back(start);
|
|
init.visited_nodes[start] = true;
|
|
pathQueue.push(init);
|
|
|
|
/* LOOP
|
|
* IF there are no paths left in the pathQueue
|
|
* THEN there is no path to the goal; exit loop
|
|
* END IF
|
|
* IF the shortest path in the pathQueue ends at the goal node,
|
|
* THEN we are done; exit loop
|
|
* END IF
|
|
* Remove the shortest path in the pathQueue
|
|
* FOR EACH transition from the last node in the shortest path
|
|
* IF the destination of the transition has not been visited
|
|
* yet in this path
|
|
* THEN Add a new path to the pathQueue with the destination
|
|
* node of the transition appended to the end of the path
|
|
* END IF
|
|
* END FOR EACH
|
|
* END LOOP
|
|
*/
|
|
for (;;)
|
|
{
|
|
if (pathQueue.size() == 0)
|
|
{
|
|
cout << "There is no path from " << start << " to " << goal
|
|
<< "!" << endl;
|
|
break;
|
|
}
|
|
if (pathQueue.top().visited_nodes.find(goal)
|
|
!= pathQueue.top().visited_nodes.end())
|
|
{
|
|
cout << "The minimum-energy set of reactions follows the path "
|
|
<< pathQueue.top() << " with a energy cost of "
|
|
<< pathQueue.top().cost
|
|
<< endl;
|
|
break;
|
|
}
|
|
Path p = pathQueue.top();
|
|
pathQueue.pop();
|
|
string last = p.node_list[p.node_list.size() - 1];
|
|
if (transitions.find(last) != transitions.end())
|
|
{
|
|
for (int i = 0, sz = transitions[last].size(); i < sz; i++)
|
|
{
|
|
if (p.visited_nodes.find(transitions[last][i].destination)
|
|
== p.visited_nodes.end())
|
|
{
|
|
/* the destination has not yet been visited in this path */
|
|
Path newPath(p, transitions[last][i]);
|
|
pathQueue.push(newPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|