// Example solution to programming assignment 5.
// CS 509, Spring 2002.
#include <vector>
#include <algorithm>
#include <numeric>
#include <iostream>
typedef std::pair<int, int> int_pair;
//
// This code needs to keep track of the indices of the pairs selected for the
// subset. One way to do this is to add the index to the pair, creating a
// triple. As the triple gets moved around, so does the index.
//
struct triple {
int first, second;
unsigned indx;
triple(int f, int s, unsigned i = 0) : first(f), second(s), indx(i) { }
};
typedef std::vector<triple> itrio_vec;
//
// Given a vector of pairs, this code needs to create a vector of triples.
// This can be done with the STL generic algorithm transform(), in conjunction
// with the functor p2t that turns a pair into a triple. The indicies are
// assigned to pairs in left to right order (that is from begin() to end()).
//
struct p2t {
unsigned indx;
p2t() : indx(0) { }
triple operator () (int_pair p) {
return triple(p.first, p.second, indx++);
}
};
//
// Deja vu, c++ style.
//
static unsigned difference_sum(const itrio_vec &);
static int first_sum(const itrio_vec &);
static bool operator > (const itrio_vec &, const itrio_vec &);
static triple trio_sum(const itrio_vec &);
static int second_sum(const itrio_vec &);
static void select(itrio_vec, itrio_vec &, itrio_vec, unsigned);
static int total_sum(const itrio_vec &);
static unsigned get_index(const triple &);
//
// The actual code to find an acceptable subset is stupid: it's a brute force
// search through all possible subsets.
//
std::vector<unsigned>
pick_pairs(const std::vector<int_pair> & pairs, unsigned n) {
// If the subset size is larger than the input set size, die with an error
// message.
if (pairs.size() < n) {
std::cerr << "The subset size " << n << " is larger than the pair set "
<< "size " << pairs.size() << ".\n";
exit(EXIT_FAILURE);
}
// Turn the pairs into triples, using the pair index as the third element.
itrio_vec trios;
std::transform(
pairs.begin(), pairs.end(), std::back_inserter(trios), p2t());
// select() needs a best subset so it has something to compare the current
// subset against. Arbitrarily select the first n elements of the input set
// as the best subset.
itrio_vec current_subset, best_subset;
std::copy(trios.begin(), trios.begin() + n,
std::back_inserter(best_subset));
// If the subset size is less then the input set size, generate all possible
// n-element subests of the input set and compare each one against the best
// n-element subset found so far.
if (pairs.size() > n)
select(current_subset, best_subset, trios, n);
// Having been compared against all possible n-element subsets, best_subset
// now really does contain the best possible subset (or among the best).
// Copy the indices from the triples to a vector and return the vector.
std::vector<unsigned> indxs;
std::transform(best_subset.begin(), best_subset.end(),
std::back_inserter(indxs), get_index);
return indxs;
}
//
// This is the usual recursive algorithm for generating subsets.
//
static void
select(
itrio_vec current_subset, itrio_vec & best_subset, itrio_vec set, unsigned n) {
// If the current subset has the proper number of elements, compare it
// against the best subset and take it as the new best subset if it's better.
// In any case, return immediately because the current subset can't grow any
// larger and still be a valid answer.
if (current_subset.size() == n) {
if (current_subset > best_subset)
best_subset = current_subset;
return;
}
// The current subset has less than n elements. If the input set is empty,
// there's nothing left to do.
if (set.empty())
return;
// Otherwise, pick an arbitrary element t of the input set as a candidate
// element for the current subset.
triple t = set.back();
set.pop_back();
// The element t either is or is not part of the best subset for this
// problem. Because we don't know which one, we have to check both
// possibilities, first without and then with.
select(current_subset, best_subset, set, n);
current_subset.push_back(t);
select(current_subset, best_subset, set, n);
}
//
// Return the difference sum of the triples in the input vector.
//
static unsigned
difference_sum(const itrio_vec & tv) {
const int d = first_sum(tv) - second_sum(tv);
return d < 0 ? -d : d;
}
//
// Return the sum of the first elements of the triples in the input vector.
//
static int
first_sum(const itrio_vec & tv) {
return trio_sum(tv).first;
}
//
// Return the index part of the input triple.
//
static unsigned
get_index(const triple & t) {
return t.indx;
}
//
// Return true if and only if triple vector tv1 is a better subset than triple
// vector tv2, where "better" is defined in the assignment. This code assumes
// the input vectors are properly sized.
static bool
operator > (const itrio_vec & tv1, const itrio_vec & tv2) {
return (difference_sum(tv1) < difference_sum(tv2)) or
((difference_sum(tv1) == difference_sum(tv2)) and
(total_sum(tv1) > total_sum(tv2)));
}
//
// Return the sum of triples it1 and it2; the indices of the triples don't
// matter.
//
static triple
operator + (const triple & it1, const triple & it2) {
return triple(it1.first + it2.first, it1.second + it2.second);
}
//
// Return the sum of the triples in the input vector.
//
static triple
trio_sum(const itrio_vec & tv) {
return std::accumulate(tv.begin(), tv.end(), triple(0, 0), operator +);
}
//
// Sum the triples in the input vector and return the second component of the
// sum.
//
static int
second_sum(const itrio_vec & tv) {
return trio_sum(tv).second;
}
//
// Return the total sum of the input vector.
//
static int
total_sum(const itrio_vec & tv) {
return first_sum(tv) + second_sum(tv);
}
// $Log: pp.cc,v $
// Revision 1.4 2002/04/18 17:17:53 rclayton
// Do the documentation thing.
//
// Revision 1.3 2002/03/26 19:33:29 rclayton
// Generate the best subset as n past begin(), not n past end().
//
// Revision 1.2 2002/03/26 16:34:41 rclayton
// Rewrote to use the subset generating solution.
//
syntax highlighted by Code2HTML, v. 0.9