61 void reserve(std::size_t request_count, std::size_t candidate_count) {
62 request_order_.reserve(request_count);
63 candidate_order_.reserve(candidate_count);
64 remaining_capacity_.reserve(candidate_count);
65 assignments_.reserve(request_count);
69 template <
typename ScoreFn>
71 std::span<const TacticalRequest> requests,
72 std::span<const TacticalCandidate> candidates, ScoreFn&& score,
75 std::vector<std::size_t> request_order_;
76 std::vector<std::size_t> candidate_order_;
77 std::vector<std::uint32_t> remaining_capacity_;
78 std::vector<TacticalAssignment> assignments_;
84 std::span<const TacticalRequest> requests,
85 std::span<const TacticalCandidate> candidates, ScoreFn&& score,
87 scratch.assignments_.clear();
88 scratch.request_order_.resize(requests.size());
89 for (std::size_t i = 0; i < requests.size(); ++i) {
90 scratch.request_order_[i] = i;
92 std::sort(scratch.request_order_.begin(), scratch.request_order_.end(),
93 [&](std::size_t lhs, std::size_t rhs) {
94 return requests[lhs].requester < requests[rhs].requester;
96 for (std::size_t i = 1; i < scratch.request_order_.size(); ++i) {
97 if (requests[scratch.request_order_[i - 1]].requester ==
98 requests[scratch.request_order_[i]].requester) {
99 return {TacticalAssignmentStatus::InvalidInput, 0, {}};
102 scratch.candidate_order_.resize(candidates.size());
103 for (std::size_t i = 0; i < candidates.size(); ++i) {
104 scratch.candidate_order_[i] = i;
106 std::sort(scratch.candidate_order_.begin(), scratch.candidate_order_.end(),
107 [&](std::size_t lhs, std::size_t rhs) {
108 return candidates[lhs].candidate < candidates[rhs].candidate;
110 for (std::size_t i = 1; i < scratch.candidate_order_.size(); ++i) {
111 if (candidates[scratch.candidate_order_[i - 1]].candidate ==
112 candidates[scratch.candidate_order_[i]].candidate) {
113 return {TacticalAssignmentStatus::InvalidInput, 0, {}};
117 scratch.assignments_.resize(requests.size());
118 for (std::size_t i = 0; i < requests.size(); ++i) {
119 scratch.assignments_[i].requester = requests[i].requester;
121 std::sort(scratch.request_order_.begin(), scratch.request_order_.end(),
122 [&](std::size_t lhs, std::size_t rhs) {
123 if (requests[lhs].priority != requests[rhs].priority) {
124 return requests[lhs].priority > requests[rhs].priority;
126 return requests[lhs].requester < requests[rhs].requester;
128 scratch.remaining_capacity_.resize(candidates.size());
129 for (std::size_t i = 0; i < candidates.size(); ++i) {
130 scratch.remaining_capacity_[i] = candidates[i].capacity;
133 auto assigned_count = std::size_t{0};
134 for (
const auto request_index : scratch.request_order_) {
135 auto best = candidates.size();
136 auto best_score = std::numeric_limits<std::int64_t>::max();
137 for (std::size_t candidate_index = 0; candidate_index < candidates.size();
139 if (scratch.remaining_capacity_[candidate_index] == 0) {
142 const auto candidate_score =
static_cast<TacticalScore>(std::invoke(
143 score, requests[request_index], candidates[candidate_index]));
144 if (!candidate_score.feasible) {
147 if (best == candidates.size() || candidate_score.value < best_score ||
148 (candidate_score.value == best_score &&
149 candidates[candidate_index].candidate <
150 candidates[best].candidate)) {
151 best = candidate_index;
152 best_score = candidate_score.value;
155 if (best == candidates.size()) {
158 --scratch.remaining_capacity_[best];
160 requests[request_index].requester,
161 candidates[best].candidate,
162 candidates[best].position,
169 const auto status = assigned_count == requests.size()
170 ? TacticalAssignmentStatus::Complete
171 : TacticalAssignmentStatus::Partial;
172 return {status, assigned_count,
173 std::span<const TacticalAssignment>{scratch.assignments_}};