1 /* -*- mode: C++; indent-tabs-mode: nil; -*-
3 * This file is a part of LEMON, a generic C++ optimization library.
5 * Copyright (C) 2003-2010
6 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
7 * (Egervary Research Group on Combinatorial Optimization, EGRES).
9 * Permission to use, modify and distribute this software is granted
10 * provided that this copyright notice appears in all copies. For
11 * precise terms see the accompanying LICENSE file.
13 * This software is provided "AS IS" with no warranty of any kind,
14 * express or implied, and with no claim as to its suitability for any
19 #ifndef LEMON_CHRISTOFIDES_TSP_H
20 #define LEMON_CHRISTOFIDES_TSP_H
24 /// \brief Christofides algorithm for symmetric TSP
26 #include <lemon/full_graph.h>
27 #include <lemon/smart_graph.h>
28 #include <lemon/kruskal.h>
29 #include <lemon/matching.h>
30 #include <lemon/euler.h>
36 /// \brief Christofides algorithm for symmetric TSP.
38 /// ChristofidesTsp implements Christofides' heuristic for solving
39 /// symmetric \ref tsp "TSP".
41 /// This a well-known approximation method for the TSP problem with
42 /// metric cost function.
43 /// It has a guaranteed approximation factor of 3/2 (i.e. it finds a tour
44 /// whose total cost is at most 3/2 of the optimum), but it usually
45 /// provides better solutions in practice.
46 /// This implementation runs in O(n<sup>3</sup>log(n)) time.
48 /// The algorithm starts with a \ref spantree "minimum cost spanning tree" and
49 /// finds a \ref MaxWeightedPerfectMatching "minimum cost perfect matching"
50 /// in the subgraph induced by the nodes that have odd degree in the
52 /// Finally, it constructs the tour from the \ref EulerIt "Euler traversal"
53 /// of the union of the spanning tree and the matching.
54 /// During this last step, the algorithm simply skips the visited nodes
55 /// (i.e. creates shortcuts) assuming that the triangle inequality holds
56 /// for the cost function.
58 /// \tparam CM Type of the cost map.
60 /// \warning CM::Value must be a signed number type.
61 template <typename CM>
66 /// Type of the cost map
68 /// Type of the edge costs
69 typedef typename CM::Value Cost;
73 GRAPH_TYPEDEFS(FullGraph);
77 std::vector<Node> _path;
82 /// \brief Constructor
85 /// \param gr The \ref FullGraph "full graph" the algorithm runs on.
86 /// \param cost The cost map.
87 ChristofidesTsp(const FullGraph &gr, const CostMap &cost)
88 : _gr(gr), _cost(cost) {}
90 /// \name Execution Control
93 /// \brief Runs the algorithm.
95 /// This function runs the algorithm.
97 /// \return The total cost of the found tour.
101 if (_gr.nodeNum() == 0) return _sum = 0;
102 else if (_gr.nodeNum() == 1) {
103 _path.push_back(_gr(0));
106 else if (_gr.nodeNum() == 2) {
107 _path.push_back(_gr(0));
108 _path.push_back(_gr(1));
109 return _sum = 2 * _cost[_gr.edge(_gr(0), _gr(1))];
112 // Compute min. cost spanning tree
113 std::vector<Edge> tree;
114 kruskal(_gr, _cost, std::back_inserter(tree));
116 FullGraph::NodeMap<int> deg(_gr, 0);
117 for (int i = 0; i != int(tree.size()); ++i) {
123 // Copy the induced subgraph of odd nodes
124 std::vector<Node> odd_nodes;
125 for (NodeIt u(_gr); u != INVALID; ++u) {
126 if (deg[u] % 2 == 1) odd_nodes.push_back(u);
130 SmartGraph::EdgeMap<Cost> scost(sgr);
131 for (int i = 0; i != int(odd_nodes.size()); ++i) {
134 for (int i = 0; i != int(odd_nodes.size()); ++i) {
135 for (int j = 0; j != int(odd_nodes.size()); ++j) {
136 if (j == i) continue;
138 sgr.addEdge(sgr.nodeFromId(i), sgr.nodeFromId(j));
139 scost[e] = -_cost[_gr.edge(odd_nodes[i], odd_nodes[j])];
143 // Compute min. cost perfect matching
144 MaxWeightedPerfectMatching<SmartGraph, SmartGraph::EdgeMap<Cost> >
148 for (SmartGraph::EdgeIt e(sgr); e != INVALID; ++e) {
149 if (mwpm.matching(e)) {
150 tree.push_back( _gr.edge(odd_nodes[sgr.id(sgr.u(e))],
151 odd_nodes[sgr.id(sgr.v(e))]) );
155 // Join the spanning tree and the matching
157 for (int i = 0; i != _gr.nodeNum(); ++i) {
160 for (int i = 0; i != int(tree.size()); ++i) {
161 int ui = _gr.id(_gr.u(tree[i])),
162 vi = _gr.id(_gr.v(tree[i]));
163 sgr.addEdge(sgr.nodeFromId(ui), sgr.nodeFromId(vi));
166 // Compute the tour from the Euler traversal
167 SmartGraph::NodeMap<bool> visited(sgr, false);
168 for (EulerIt<SmartGraph> e(sgr); e != INVALID; ++e) {
169 SmartGraph::Node n = sgr.target(e);
171 _path.push_back(_gr(sgr.id(n)));
176 _sum = _cost[_gr.edge(_path.back(), _path.front())];
177 for (int i = 0; i < int(_path.size())-1; ++i) {
178 _sum += _cost[_gr.edge(_path[i], _path[i+1])];
186 /// \name Query Functions
189 /// \brief The total cost of the found tour.
191 /// This function returns the total cost of the found tour.
193 /// \pre run() must be called before using this function.
194 Cost tourCost() const {
198 /// \brief Returns a const reference to the node sequence of the
201 /// This function returns a const reference to a vector
202 /// that stores the node sequence of the found tour.
204 /// \pre run() must be called before using this function.
205 const std::vector<Node>& tourNodes() const {
209 /// \brief Gives back the node sequence of the found tour.
211 /// This function copies the node sequence of the found tour into
212 /// an STL container through the given output iterator. The
213 /// <tt>value_type</tt> of the container must be <tt>FullGraph::Node</tt>.
216 /// std::vector<FullGraph::Node> nodes(countNodes(graph));
217 /// tsp.tourNodes(nodes.begin());
221 /// std::list<FullGraph::Node> nodes;
222 /// tsp.tourNodes(std::back_inserter(nodes));
225 /// \pre run() must be called before using this function.
226 template <typename Iterator>
227 void tourNodes(Iterator out) const {
228 std::copy(_path.begin(), _path.end(), out);
231 /// \brief Gives back the found tour as a path.
233 /// This function copies the found tour as a list of arcs/edges into
234 /// the given \ref concept::Path "path structure".
236 /// \pre run() must be called before using this function.
237 template <typename Path>
238 void tour(Path &path) const {
240 for (int i = 0; i < int(_path.size()) - 1; ++i) {
241 path.addBack(_gr.arc(_path[i], _path[i+1]));
243 if (int(_path.size()) >= 2) {
244 path.addBack(_gr.arc(_path.back(), _path.front()));
252 }; // namespace lemon