/* File: tsp_search.c * Purpose: Use depth-first search to solve an instance of the * travelling salesman problem. * * Input: From a user-specified file, the number of cities * followed by the costs of travelling between the * cities organized as a matrix: the cost of * travelling from city i to city j is the ij entry. * Output: The best tour found by the program and the cost * of the tour. * * Compile: gcc -g -Wall -o tsp_search tsp_search.c * Usage: tsp_search * * Notes: * 1. Weights and cities are non-negative ints. * 2. Program assumes the cost of travelling from a city to * itself is zero, and the cost of travelling from one * city to another city is positive. * 3. Note that costs may not be symmetric: the cost of travelling * from A to B, may, in general, be different from the cost * of travelling from B to A. * 4. Salesperson's home town is 0. */ #include #include const int INFINITY = 1000000; const int NO_CITY = -1; const int FALSE = 0; const int TRUE = 1; typedef int city_t; typedef int weight_t; typedef struct { city_t* cities; /* Cities in partial tour */ int count; /* Number of cities in partial tour */ weight_t cost; /* Cost of partial tour */ } tour_t; void Usage(char* prog_name); void Read_mat(FILE* mat_file, weight_t** mat_p, int* n_p); void Print_mat(weight_t* mat, int n); void Search(city_t city, weight_t cost, weight_t mat[], int n, tour_t* tour_p, tour_t* best_tour_p); void Print_tour(tour_t* tour_p, char* title); void Check_best_tour(city_t city, weight_t mat[], int n, tour_t* tour_p, tour_t* best_tour_p); int Feasible(weight_t mat[], int n, city_t city, city_t nbr, tour_t* tour_p, tour_t* best_tour_p); int Visited(city_t nbr, tour_t* tour_p); void Init_tour(tour_t* tour_p, int n, weight_t cost); /*------------------------------------------------------------------*/ int main(int argc, char* argv[]) { int n; weight_t *mat; FILE* mat_file; tour_t best_tour; tour_t tour; if (argc != 2) Usage(argv[0]); mat_file = fopen(argv[1], "r"); if (mat_file == NULL) { fprintf(stderr, "Can't open %s\n", argv[1]); Usage(argv[0]); } Read_mat(mat_file, &mat, &n); fclose(mat_file); # ifdef DEBUG Print_mat(mat, n); # endif Init_tour(&best_tour, n, INFINITY); Init_tour(&tour, n, 0); Search(0, 0, mat, n, &tour, &best_tour); Print_tour(&best_tour, "Best tour"); printf("Cost = %d\n", best_tour.cost); free(best_tour.cities); free(tour.cities); free(mat); return 0; } /* main */ /*------------------------------------------------------------------ * Function: Init_tour * Purpose: Allocate storage for the cities on the tour, and * initialize the data members * In args: * n: number of cities in complete tour * cost: initial cost of tour * * Out arg: tour_p */ void Init_tour(tour_t* tour_p, int n, weight_t cost) { int i; tour_p->cities = malloc((n+1)*sizeof(city_t)); for (i = 0; i <= n; i++) { tour_p->cities[i] = NO_CITY; } tour_p->cost = cost; tour_p->count = 0; } /* Init_tour */ /*------------------------------------------------------------------ * Function: Usage * Purpose: Inform user how to start program and exit * In arg: prog_name */ void Usage(char* prog_name) { fprintf(stderr, "usage: %s \n", prog_name); exit(0); } /* Usage */ /*------------------------------------------------------------------ * Function: Read_mat * Purpose: Read in the number of cities and the matrix of costs * In arg: mat_file * Out args: mat_p, n_p */ void Read_mat(FILE* mat_file, weight_t** mat_p, int* n_p) { int n, i, j; weight_t *mat; fscanf(mat_file, "%d", n_p); n = *n_p; *mat_p = malloc(n*n*sizeof(weight_t)); mat = *mat_p; for (i = 0; i < n; i++) for (j = 0; j < n; j++) fscanf(mat_file, "%d", &mat[n*i+j]); } /* Read_mat */ /*------------------------------------------------------------------ * Function: Print_mat * Purpose: Print the number of cities and the matrix of costs * In args: All */ void Print_mat(weight_t* mat, int n) { int i, j; printf("Order = %d\n", n); printf("Matrix = \n"); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) printf("%2d ", mat[i*n+j]); printf("\n"); } printf("\n"); } /* Print_mat */ /*------------------------------------------------------------------ * Function: Search * Purpose: Recursively search for an optimal tour * In args: city: city to be visited on this tour * cost: cost of visiting this city from previously * visited city (0 if first city on tour) * mat: adjacency matrix of cities * n: number of cities * In/out args: tour_p: on input and output current tour without input city, * during execution it's updated to include input city * and then returned to its original state * best_tour_p: on input current best tour * on output either same tour, or current tour */ void Search(city_t city, weight_t cost, weight_t mat[], int n, tour_t* tour_p, tour_t* best_tour_p) { city_t nbr; tour_p->cost += cost; tour_p->cities[tour_p->count] = city; tour_p->count++; if (tour_p->count == n) { Check_best_tour(city, mat, n, tour_p, best_tour_p); } else { for (nbr = 1; nbr < n; nbr++) if (Feasible(mat, n, city, nbr, tour_p, best_tour_p)) { Search(nbr, mat[n*city+nbr], mat, n, tour_p, best_tour_p); } } tour_p->count--; tour_p->cities[tour_p->count] = NO_CITY; tour_p->cost -= cost; } /* Search */ /*------------------------------------------------------------------ * Function: Feasible * Purpose: Check whether nbr could possibly lead to a better * solution if it is added to the current tour. The * function checks whether nbr has already been visited * in the current tour, and, if not, whether adding the * edge from the current city to nbr will result in * a cost less than the current best cost. * In args: All * Return: TRUE if the nbr can be added to the current tour. * FALSE otherwise */ int Feasible(weight_t mat[], int n, city_t city, city_t nbr, tour_t* tour_p, tour_t* best_tour_p) { if (!Visited(nbr, tour_p) && tour_p -> cost + mat[n*city + nbr] < best_tour_p->cost) return TRUE; else return FALSE; } /* Feasible */ /*------------------------------------------------------------------ * Function: Visited * Purpose: Use linear search to determine whether nbr has already * been visited on the current tour. * In args: All * Return val: TRUE if nbr has already been visited. * FALSE otherwise */ int Visited(city_t nbr, tour_t* tour_p) { int i; for (i = 0; i < tour_p->count; i++) if ( tour_p->cities[i] == nbr ) return TRUE; return FALSE; } /* Visited */ /*------------------------------------------------------------------ * Function: Print_tour * Purpose: Print the tour * In args: All */ void Print_tour(tour_t* tour_p, char* title) { int i; printf("%s:\n", title); for (i = 0; i < tour_p->count; i++) printf("%d ", tour_p->cities[i]); printf("\n\n"); } /* Print_tour */ /*------------------------------------------------------------------ * Function: Check_best_tour * Purpose: Determine whether the current n-city tour will be * better than the current best tour. If so, update * best_tour_p * In args: city, mat, n, tour_p * In/out arg: best_tour_p */ void Check_best_tour(city_t city, weight_t mat[], int n, tour_t* tour_p, tour_t* best_tour_p) { int i; /* Need to add in cost of returning to 0 */ if (tour_p->cost + mat[city*n + 0] < best_tour_p->cost) { for (i = 0; i < tour_p->count; i++) best_tour_p->cities[i] = tour_p->cities[i]; best_tour_p->cities[n] = 0; best_tour_p->count = n+1; best_tour_p->cost = tour_p->cost + mat[city*n + 0]; } } /* Check_best_tour */