148 lines
5.4 KiB
C++
148 lines
5.4 KiB
C++
// Copyright 2010-2025 Google LLC
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Simple interface to solve the linear sum assignment problem. It
|
|
// uses about twice as much memory as directly using the
|
|
// LinearSumAssignment class template, but it is as fast and presents
|
|
// a simpler interface. This is the class you should use in most
|
|
// situations.
|
|
//
|
|
// The assignment problem: Given N "left" nodes and N "right" nodes,
|
|
// and a set of left->right arcs with integer costs, find a perfect
|
|
// matching (i.e., each "left" node is assigned to one "right" node)
|
|
// that minimizes the overall cost.
|
|
//
|
|
// Example usage:
|
|
//
|
|
// #include "ortools/graph/assignment.h"
|
|
//
|
|
// SimpleLinearSumAssignment assignment;
|
|
// for (int arc = 0; arc < num_arcs; ++arc) {
|
|
// assignment.AddArcWithCost(head(arc), tail(arc), cost(arc));
|
|
// }
|
|
// if (assignment.Solve() == SimpleLinearSumAssignment::OPTIMAL) {
|
|
// printf("A perfect matching exists.\n");
|
|
// printf("The best possible cost is %d.\n", assignment.OptimalCost());
|
|
// printf("An optimal assignment is:\n");
|
|
// for (int node = 0; node < assignment.NumNodes(); ++node) {
|
|
// printf("left node %d assigned to right node %d with cost %d.\n",
|
|
// node,
|
|
// assignment.RightMate(node),
|
|
// assignment.AssignmentCost(node));
|
|
// }
|
|
// printf("Note that it may not be the unique optimal assignment.");
|
|
// } else {
|
|
// printf("There is an issue with the input or no perfect matching exists.");
|
|
// }
|
|
|
|
#ifndef OR_TOOLS_GRAPH_ASSIGNMENT_H_
|
|
#define OR_TOOLS_GRAPH_ASSIGNMENT_H_
|
|
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
namespace operations_research {
|
|
|
|
class SimpleLinearSumAssignment {
|
|
public:
|
|
typedef int32_t NodeIndex;
|
|
typedef int32_t ArcIndex;
|
|
typedef int64_t CostValue;
|
|
|
|
// The constructor takes no size.
|
|
// New node indices will be created lazily by AddArcWithCost().
|
|
SimpleLinearSumAssignment();
|
|
|
|
#ifndef SWIG
|
|
// This type is neither copyable nor movable.
|
|
SimpleLinearSumAssignment(const SimpleLinearSumAssignment&) = delete;
|
|
SimpleLinearSumAssignment& operator=(const SimpleLinearSumAssignment&) =
|
|
delete;
|
|
#endif
|
|
|
|
// Reserves space for the given number of arcs, to avoid reallocation in
|
|
// `AddArcWithCost().
|
|
void ReserveArcs(ArcIndex num_arcs) {
|
|
arc_tail_.reserve(num_arcs);
|
|
arc_head_.reserve(num_arcs);
|
|
arc_cost_.reserve(num_arcs);
|
|
}
|
|
|
|
// Adds an arc from a left node to a right node with a given cost.
|
|
// * Node indices must be non-negative (>= 0). For a perfect
|
|
// matching to exist on n nodes, the values taken by "left_node"
|
|
// must cover [0, n), same for "right_node".
|
|
// * The arc cost can be any integer, negative, positive or zero.
|
|
// * After the method finishes, NumArcs() == the returned ArcIndex + 1.
|
|
ArcIndex AddArcWithCost(NodeIndex left_node, NodeIndex right_node,
|
|
CostValue cost);
|
|
|
|
// Returns the current number of left nodes which is the same as the
|
|
// number of right nodes. This is one greater than the largest node
|
|
// index seen so far in AddArcWithCost().
|
|
NodeIndex NumNodes() const;
|
|
|
|
// Returns the current number of arcs in the graph.
|
|
ArcIndex NumArcs() const;
|
|
|
|
// Returns user-provided data.
|
|
// The implementation will crash if "arc" is not in [0, NumArcs()).
|
|
NodeIndex LeftNode(ArcIndex arc) const;
|
|
NodeIndex RightNode(ArcIndex arc) const;
|
|
CostValue Cost(ArcIndex arc) const;
|
|
|
|
// Solves the problem (finds the perfect matching that minimizes the
|
|
// cost) and returns the solver status.
|
|
enum Status {
|
|
OPTIMAL, // The algorithm found a minimum-cost perfect matching.
|
|
INFEASIBLE, // The given problem admits no perfect matching.
|
|
POSSIBLE_OVERFLOW, // Some cost magnitude is too large.
|
|
};
|
|
Status Solve();
|
|
|
|
// Returns the cost of an assignment with minimal cost.
|
|
// This is 0 if the last Solve() didn't return OPTIMAL.
|
|
CostValue OptimalCost() const { return optimal_cost_; }
|
|
|
|
// Returns the right node assigned to the given left node in the
|
|
// last solution computed by Solve(). This works only if Solve()
|
|
// returned OPTIMAL.
|
|
//
|
|
// Note: It is possible that there is more than one optimal
|
|
// solution. The algorithm is deterministic so it will always return
|
|
// the same solution for a given problem. There is no such guarantee
|
|
// from one code version to the next, but the code does not change
|
|
// often.
|
|
NodeIndex RightMate(NodeIndex left_node) const {
|
|
return arc_head_[assignment_arcs_[left_node]];
|
|
}
|
|
|
|
// Returns the cost of the arc used for "left_node"'s assignment.
|
|
// This works only if Solve() returned OPTIMAL.
|
|
CostValue AssignmentCost(NodeIndex left_node) const {
|
|
return arc_cost_[assignment_arcs_[left_node]];
|
|
}
|
|
|
|
private:
|
|
NodeIndex num_nodes_;
|
|
std::vector<NodeIndex> arc_tail_;
|
|
std::vector<NodeIndex> arc_head_;
|
|
std::vector<CostValue> arc_cost_;
|
|
std::vector<ArcIndex> assignment_arcs_;
|
|
CostValue optimal_cost_;
|
|
};
|
|
|
|
} // namespace operations_research
|
|
|
|
#endif // OR_TOOLS_GRAPH_ASSIGNMENT_H_
|