kpeter@1201: /* -*- mode: C++; indent-tabs-mode: nil; -*- kpeter@1201: * kpeter@1201: * This file is a part of LEMON, a generic C++ optimization library. kpeter@1201: * alpar@1270: * Copyright (C) 2003-2013 kpeter@1201: * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport kpeter@1201: * (Egervary Research Group on Combinatorial Optimization, EGRES). kpeter@1201: * kpeter@1201: * Permission to use, modify and distribute this software is granted kpeter@1201: * provided that this copyright notice appears in all copies. For kpeter@1201: * precise terms see the accompanying LICENSE file. kpeter@1201: * kpeter@1201: * This software is provided "AS IS" with no warranty of any kind, kpeter@1201: * express or implied, and with no claim as to its suitability for any kpeter@1201: * purpose. kpeter@1201: * kpeter@1201: */ kpeter@1201: f4c3@1199: #ifndef LEMON_CHRISTOFIDES_TSP_H f4c3@1199: #define LEMON_CHRISTOFIDES_TSP_H f4c3@1199: kpeter@1201: /// \ingroup tsp kpeter@1201: /// \file kpeter@1201: /// \brief Christofides algorithm for symmetric TSP kpeter@1201: f4c3@1199: #include f4c3@1199: #include f4c3@1199: #include f4c3@1199: #include f4c3@1199: #include f4c3@1199: f4c3@1199: namespace lemon { alpar@1270: kpeter@1202: /// \ingroup tsp kpeter@1202: /// kpeter@1201: /// \brief Christofides algorithm for symmetric TSP. kpeter@1201: /// kpeter@1201: /// ChristofidesTsp implements Christofides' heuristic for solving kpeter@1201: /// symmetric \ref tsp "TSP". kpeter@1201: /// kpeter@1201: /// This a well-known approximation method for the TSP problem with kpeter@1202: /// metric cost function. kpeter@1204: /// It has a guaranteed approximation factor of 3/2 (i.e. it finds a tour kpeter@1204: /// whose total cost is at most 3/2 of the optimum), but it usually kpeter@1204: /// provides better solutions in practice. kpeter@1201: /// This implementation runs in O(n3log(n)) time. kpeter@1201: /// kpeter@1201: /// The algorithm starts with a \ref spantree "minimum cost spanning tree" and kpeter@1201: /// finds a \ref MaxWeightedPerfectMatching "minimum cost perfect matching" kpeter@1201: /// in the subgraph induced by the nodes that have odd degree in the kpeter@1201: /// spanning tree. kpeter@1201: /// Finally, it constructs the tour from the \ref EulerIt "Euler traversal" kpeter@1201: /// of the union of the spanning tree and the matching. kpeter@1201: /// During this last step, the algorithm simply skips the visited nodes kpeter@1201: /// (i.e. creates shortcuts) assuming that the triangle inequality holds kpeter@1201: /// for the cost function. kpeter@1201: /// kpeter@1201: /// \tparam CM Type of the cost map. kpeter@1201: /// kpeter@1202: /// \warning CM::Value must be a signed number type. f4c3@1199: template kpeter@1201: class ChristofidesTsp kpeter@1201: { kpeter@1201: public: kpeter@1201: kpeter@1201: /// Type of the cost map kpeter@1201: typedef CM CostMap; kpeter@1201: /// Type of the edge costs kpeter@1201: typedef typename CM::Value Cost; kpeter@1201: f4c3@1199: private: kpeter@1201: kpeter@1201: GRAPH_TYPEDEFS(FullGraph); kpeter@1201: kpeter@1201: const FullGraph &_gr; kpeter@1201: const CostMap &_cost; kpeter@1201: std::vector _path; kpeter@1201: Cost _sum; f4c3@1199: f4c3@1199: public: f4c3@1199: kpeter@1201: /// \brief Constructor kpeter@1201: /// kpeter@1201: /// Constructor. kpeter@1201: /// \param gr The \ref FullGraph "full graph" the algorithm runs on. kpeter@1201: /// \param cost The cost map. kpeter@1201: ChristofidesTsp(const FullGraph &gr, const CostMap &cost) kpeter@1201: : _gr(gr), _cost(cost) {} kpeter@1201: kpeter@1201: /// \name Execution Control kpeter@1201: /// @{ kpeter@1201: kpeter@1201: /// \brief Runs the algorithm. kpeter@1201: /// kpeter@1201: /// This function runs the algorithm. kpeter@1201: /// kpeter@1201: /// \return The total cost of the found tour. f4c3@1199: Cost run() { f4c3@1199: _path.clear(); kpeter@1201: kpeter@1201: if (_gr.nodeNum() == 0) return _sum = 0; kpeter@1201: else if (_gr.nodeNum() == 1) { kpeter@1201: _path.push_back(_gr(0)); kpeter@1201: return _sum = 0; kpeter@1201: } kpeter@1201: else if (_gr.nodeNum() == 2) { kpeter@1201: _path.push_back(_gr(0)); kpeter@1201: _path.push_back(_gr(1)); kpeter@1201: return _sum = 2 * _cost[_gr.edge(_gr(0), _gr(1))]; kpeter@1201: } alpar@1270: kpeter@1201: // Compute min. cost spanning tree kpeter@1201: std::vector tree; kpeter@1201: kruskal(_gr, _cost, std::back_inserter(tree)); alpar@1270: kpeter@1201: FullGraph::NodeMap deg(_gr, 0); kpeter@1201: for (int i = 0; i != int(tree.size()); ++i) { kpeter@1201: Edge e = tree[i]; kpeter@1201: ++deg[_gr.u(e)]; kpeter@1201: ++deg[_gr.v(e)]; kpeter@1201: } kpeter@1201: kpeter@1201: // Copy the induced subgraph of odd nodes kpeter@1201: std::vector odd_nodes; kpeter@1201: for (NodeIt u(_gr); u != INVALID; ++u) { kpeter@1201: if (deg[u] % 2 == 1) odd_nodes.push_back(u); kpeter@1201: } alpar@1270: kpeter@1201: SmartGraph sgr; kpeter@1201: SmartGraph::EdgeMap scost(sgr); kpeter@1201: for (int i = 0; i != int(odd_nodes.size()); ++i) { kpeter@1201: sgr.addNode(); kpeter@1201: } kpeter@1201: for (int i = 0; i != int(odd_nodes.size()); ++i) { kpeter@1201: for (int j = 0; j != int(odd_nodes.size()); ++j) { kpeter@1201: if (j == i) continue; kpeter@1201: SmartGraph::Edge e = kpeter@1201: sgr.addEdge(sgr.nodeFromId(i), sgr.nodeFromId(j)); kpeter@1201: scost[e] = -_cost[_gr.edge(odd_nodes[i], odd_nodes[j])]; f4c3@1199: } f4c3@1199: } alpar@1270: kpeter@1201: // Compute min. cost perfect matching kpeter@1201: MaxWeightedPerfectMatching > kpeter@1201: mwpm(sgr, scost); kpeter@1201: mwpm.run(); alpar@1270: kpeter@1201: for (SmartGraph::EdgeIt e(sgr); e != INVALID; ++e) { kpeter@1201: if (mwpm.matching(e)) { kpeter@1201: tree.push_back( _gr.edge(odd_nodes[sgr.id(sgr.u(e))], kpeter@1201: odd_nodes[sgr.id(sgr.v(e))]) ); f4c3@1199: } f4c3@1199: } alpar@1270: alpar@1270: // Join the spanning tree and the matching kpeter@1201: sgr.clear(); kpeter@1201: for (int i = 0; i != _gr.nodeNum(); ++i) { kpeter@1201: sgr.addNode(); kpeter@1201: } kpeter@1201: for (int i = 0; i != int(tree.size()); ++i) { kpeter@1201: int ui = _gr.id(_gr.u(tree[i])), kpeter@1201: vi = _gr.id(_gr.v(tree[i])); kpeter@1201: sgr.addEdge(sgr.nodeFromId(ui), sgr.nodeFromId(vi)); kpeter@1201: } kpeter@1201: kpeter@1201: // Compute the tour from the Euler traversal kpeter@1201: SmartGraph::NodeMap visited(sgr, false); kpeter@1201: for (EulerIt e(sgr); e != INVALID; ++e) { kpeter@1201: SmartGraph::Node n = sgr.target(e); kpeter@1201: if (!visited[n]) { kpeter@1201: _path.push_back(_gr(sgr.id(n))); kpeter@1201: visited[n] = true; f4c3@1199: } f4c3@1199: } f4c3@1199: kpeter@1201: _sum = _cost[_gr.edge(_path.back(), _path.front())]; kpeter@1201: for (int i = 0; i < int(_path.size())-1; ++i) { kpeter@1201: _sum += _cost[_gr.edge(_path[i], _path[i+1])]; kpeter@1201: } f4c3@1199: f4c3@1199: return _sum; f4c3@1199: } f4c3@1199: kpeter@1201: /// @} alpar@1270: kpeter@1201: /// \name Query Functions kpeter@1201: /// @{ alpar@1270: kpeter@1201: /// \brief The total cost of the found tour. kpeter@1201: /// kpeter@1201: /// This function returns the total cost of the found tour. kpeter@1201: /// kpeter@1201: /// \pre run() must be called before using this function. kpeter@1201: Cost tourCost() const { f4c3@1199: return _sum; f4c3@1199: } alpar@1270: kpeter@1201: /// \brief Returns a const reference to the node sequence of the kpeter@1201: /// found tour. kpeter@1201: /// kpeter@1202: /// This function returns a const reference to a vector kpeter@1201: /// that stores the node sequence of the found tour. kpeter@1201: /// kpeter@1201: /// \pre run() must be called before using this function. kpeter@1201: const std::vector& tourNodes() const { kpeter@1201: return _path; kpeter@1201: } f4c3@1199: kpeter@1201: /// \brief Gives back the node sequence of the found tour. kpeter@1201: /// kpeter@1201: /// This function copies the node sequence of the found tour into kpeter@1205: /// an STL container through the given output iterator. The kpeter@1205: /// value_type of the container must be FullGraph::Node. kpeter@1205: /// For example, kpeter@1205: /// \code kpeter@1205: /// std::vector nodes(countNodes(graph)); kpeter@1205: /// tsp.tourNodes(nodes.begin()); kpeter@1205: /// \endcode kpeter@1205: /// or kpeter@1205: /// \code kpeter@1205: /// std::list nodes; kpeter@1205: /// tsp.tourNodes(std::back_inserter(nodes)); kpeter@1205: /// \endcode kpeter@1201: /// kpeter@1201: /// \pre run() must be called before using this function. kpeter@1205: template kpeter@1205: void tourNodes(Iterator out) const { kpeter@1205: std::copy(_path.begin(), _path.end(), out); kpeter@1201: } alpar@1270: kpeter@1201: /// \brief Gives back the found tour as a path. kpeter@1201: /// kpeter@1201: /// This function copies the found tour as a list of arcs/edges into alpar@1250: /// the given \ref lemon::concepts::Path "path structure". kpeter@1201: /// kpeter@1201: /// \pre run() must be called before using this function. kpeter@1201: template kpeter@1201: void tour(Path &path) const { kpeter@1201: path.clear(); kpeter@1201: for (int i = 0; i < int(_path.size()) - 1; ++i) { kpeter@1201: path.addBack(_gr.arc(_path[i], _path[i+1])); kpeter@1201: } kpeter@1201: if (int(_path.size()) >= 2) { kpeter@1201: path.addBack(_gr.arc(_path.back(), _path.front())); kpeter@1201: } kpeter@1201: } alpar@1270: kpeter@1201: /// @} alpar@1270: f4c3@1199: }; f4c3@1199: f4c3@1199: }; // namespace lemon f4c3@1199: f4c3@1199: #endif