/*
Filename: tsp2.cpp
Author: Br. David Carlson with Daniel Kissel
Date: January 11, 2002
Revised: May 25th, 2017 by Daniel Kissel
July 3, 2017 by Br. David Carlson
This software is made available under the license found
at http://cis.stvincent.edu/carlsond/cs171/tsp.html.
This program solves the travelling salesperson problem
(TSP) by enumerating all the possible tours of the cities
and choosing the best one. To use the program, place your
data in a text file and see that it follows the format
shown in the following example:
4
0 9 5 3
9 0 4 8
5 4 0 6
3 8 6 0
Note that the number of cities (here 4) is placed on the
first line by itself. Following that is a symmetric matrix
giving the costs for travelling between all pairs of cities.
Thus the (i,j) entry in the matrix gives the cost of going
from city i to city j. The cities are assumed to be numbered
from 0 through 4 - 1 = 3, in this case.
The program will ask the user for the name of the data
file to open.
The output of the program shows the lowest cost tour of
all cities with city 0 being the starting point and ending
point. It also displays the total number of tours that were
checked and the number of additions and comparisons used.
Backtracking is used in order to avoid some needless additions.
In the above example, after the tour of cities 0, 1, 2, 3 is
checked, the program would backtrack to the partial tour 0, 1, 2
to see if there is another choice for the last city. There is
not. Thus the program would backtrack to the partial tour 0, 1
and find another choice (city 3) for the third city. The addition
of the original cost 0 and the cost to go from city 0 to city 1
is not recalculated thanks to this backtracking algorithm. Rather,
the new cost to go from city 1 to city 3 is simply added to the
existing partial sum, etc.
*/
#include
#include // for setw
#include
#include // for LONG_MAX
using namespace std;
const int MaxCities = 100;
const int MaxString = 64;
/* A CityList variable will be used to keep track of the list of
cities chosen so far. The integers representing the cities
will be at indices 0, 1, 2,...,MaxCities - 1 in the Info field.
*/
typedef int CityData[MaxCities];
typedef struct
{
CityData Info;
} CityList;
/* An AvailList variable will be used to keep track of what cities
are available. A true at index k in the Info field says that city k
is available, otherwise city k is not available.
*/
typedef bool AvailData[MaxCities];
typedef struct
{
AvailData Info;
} AvailList;
/* Note that CityList and AvailList are structs so that call by copy
can be used when passing variables of these types as parameters.
*/
typedef int CostArray[MaxCities][MaxCities];
typedef char StringType[MaxString];
// Function prototypes:
void ReadData(int & NumCities, CostArray Cost);
void Choose(int Num, AvailList Avail, CityList Chosen, long Cost);
void ArrayCopy(CityList & Destination, const CityList & Source);
// Some global variables are used for speed:
int NumCities;
double NumAdditions, NumTours, NumCompares;
long MinCost;
CityList BestTour;
CostArray Cost;
int main(void)
{
AvailList Avail;
CityList Tour;
int k;
ReadData(NumCities, Cost);
cout << "Solving TSP for number of cities: " << NumCities << endl << endl;
NumAdditions = NumTours = NumCompares = 0;
MinCost = LONG_MAX;
Tour.Info[0] = 0;
Avail.Info[0] = false;
for (k = 1; k < NumCities; k++)
Avail.Info[k] = true;
Choose(1, Avail, Tour, 0);
if (MinCost < LONG_MAX)
{
cout << "The lowest cost was: " << MinCost << endl;
cout << "This was for the following tour of the cities:" << endl;
for (k = 0; k < NumCities; k++)
cout << setw(4) << BestTour.Info[k];
}
else
cout << "No solution was found" << endl;
cout << endl << endl;
cout << "Total number of tours checked was: " << NumTours << endl;
cout << "Total number of additions used was: " << NumAdditions << endl;
cout << "Total number of comparisons used was: " << NumCompares << endl;
cout << "Total number of additions and comparisons was: " << NumAdditions + NumCompares << endl;
//added in 2017 to pause the program
cout << "Press any key to exit the program..." << endl;
cin.ignore();
return 0;
}
/* Given: NumChosen The number of cities already chosen in parameter Chosen.
Avail Array showing what cities are still not chosen.
Chosen Partial tour (list of cities) constructed so far.
CostSoFar Cost of the partial tour contained in parameter Chosen.
Task: To go through all possible ways of choosing cities to complete the
tour, keeping track of the cheapest so far.
Return: The global values BestTour and MinCost giving the least cost tour
and its cost, MinCost.
*/
void Choose(int NumChosen, AvailList Avail, CityList Chosen, long CostSoFar)
{
int m;
long NewCost;
NumCompares++;
if (NumChosen == NumCities) // stopping case, all cities have been chosen
{
// Add the cost of returning to city 0 from last city:
NewCost = CostSoFar + Cost[0][Chosen.Info[NumCities - 1]];
NumAdditions++;
NumTours++;
NumCompares++;
if (NewCost < MinCost)
{
ArrayCopy(BestTour, Chosen);
MinCost = NewCost;
}
}
else // recursive case
{
for (m = 0; m < NumCities; m++) // try each possibility
{
NumCompares++;
if (Avail.Info[m])
{
Chosen.Info[NumChosen] = m;
Avail.Info[m] = false;
NewCost = CostSoFar + Cost[m][Chosen.Info[NumChosen - 1]];
NumAdditions++;
Choose(NumChosen + 1, Avail, Chosen, NewCost);
Avail.Info[m] = true;
}
}
}
}
/* Given: Nothing.
Task: To prompt the user for the data file to open, to open that file,
and to read from it the number of cities and then the matrix of
costs to travel between pairs of cities. The program is halted
if the file cannot be opened or the number of cities is too small
or too large.
Return: NumCities The number of cities specified in the data file.
Cost The matrix of costs.
*/
void ReadData(int & NumCities, CostArray Cost)
{
StringType FileName;
fstream fs;
int Row, Column;
cout << "Enter the name of the TSP data file to be read: ";
cin.getline(FileName, MaxString);
fs.open(FileName, ios::in);
if (fs.fail())
{
cerr << "Error: Could not open file: " << FileName << endl;
//added in 2017 to pause the program
cout << "Press any key to exit the program..." << endl;
cin.ignore();
exit(1);
}
fs >> NumCities;
if ((NumCities > MaxCities) || (NumCities < 2))
{
cout << "Error: Number of cities must be from 2 to " << MaxCities << endl;
//added in 2017 to pause the program
cout << "Press any key to exit the program..." << endl;
cin.ignore();
exit(1);
}
else
{
for (Row = 0; Row < NumCities; Row++)
for (Column = 0; Column < NumCities; Column++)
fs >> Cost[Row][Column];
}
fs.close();
}
/* Given: Source A CityList containing an array of cities (with the
number of cities given by the global variable NumCities
and with the first city at index 0).
Task: To copy the data from Source to Destination.
Return: Destination A CityList containing a copy of the data from Source.
*/
void ArrayCopy(CityList & Destination, const CityList & Source)
{
int k;
for (k = 0; k < NumCities; k++)
Destination.Info[k] = Source.Info[k];
}