Самый простой вариант: генерировать все перестановки произведений, каждую перестановку разбить на всевозможные варианты томов. Минусы: тома с разным порядком произведений считаются разными томами, одинаковые тома в разном порядке считаются разными результатами.
Вариант реализации с перестановками через алгоритмы и рекурсивным разбиением на тома:
#include <vector>
#include <limits>
#include <iostream>
#include <tuple>
#include <algorithm>
typedef std::pair<int, std::string> Book;
const int N = 300; //Максимум страниц
int result = std::numeric_limits<int>::max(); //Минимальное число томов
std::vector<std::vector<Book>> result_seq; //Вектора с последовательностями томов
std::vector<std::vector<int>> result_indexes; //Вектора с индексами, обзначающими конец томов
//Вывод последовательности разбиения книг по томам
void show_seq(const std::vector<Book>& split_seq, const std::vector<int>& split_indexes) {
auto it = split_indexes.begin();
for (int i = 0; i < split_seq.size(); i++) {
std::cout << split_seq[i].first << ' ' << split_seq[i].second << ' ';
if (it != split_indexes.end() && *it == i) {
std::cout << "| ";
++it;
}
}
std::cout << '\n';
}
//Разбиение последовательности книг на тома
void split_books(const std::vector<Book>& split_seq, const std::vector<int>& split_indexes = std::vector<int>()) {
int split_pages_count = 0;
for (int i = split_indexes.size() ? split_indexes.back()+1 : 0; i < split_seq.size(); ++i) {
split_pages_count+=split_seq[i].first;
if (split_pages_count > N) {
return; //Том не последний. Все возможные варианты уже сгенерированы
}
auto temp_indexes = split_indexes;
temp_indexes.push_back(i);
split_books(split_seq, temp_indexes);
}
//Выход из цикла происходит только когда вся последовательность разбита на тома
if (split_pages_count > 0) {
show_seq(split_seq, split_indexes);
if (split_indexes.size() < result) {
result_seq.clear();
result_indexes.clear();
result_seq.push_back(split_seq);
result_indexes.push_back(split_indexes);
result = split_indexes.size();
} else if (result == split_indexes.size()) {
result_seq.push_back(split_seq);
result_indexes.push_back(split_indexes);
}
}
}
int main() {
std::vector<Book> books = {{100, "title1"}, {200, "title2"}, {250, "title3"}, {50, "title4"}};
std::sort(books.begin(), books.end()); //Перебор идёт в лексикографическом порядке
do {
split_books(books); //Разбиваем на тома
} while (std::next_permutation(books.begin(), books.end())); //Генерируем следующую перестановку томов
std::cout << "Minimum number of volumes:\n";
for (int i = 0; i < result_seq.size(); i++) {
show_seq(result_seq[i], result_indexes[i]);
}
}
Избавиться от минусов можно отбрасыванием тех вариантов, где произведения внутри томов идут не по возрастанию, и тех вариантов, где первые произведения в томах идут не по возрастанию:
void split_books(const std::vector<Book>& split_seq, const std::vector<int>& split_indexes = std::vector<int>(), const Book& prev_volume = Book{-1, ""}) {
int split_pages_count = 0;
int first_index = split_indexes.size() ? split_indexes.back() + 1 : 0;
if (first_index < split_seq.size() && prev_volume > split_seq[first_index]) {
return; //Тома идут не по возрастанию первых произведений.
}
for (int i = first_index; i < split_seq.size(); ++i) {
split_pages_count+=split_seq[i].first;
if (split_pages_count > N) {
return; //Том не последний. Все возможные варианты уже сгенерированы
}
if (i > first_index && split_seq[i-1] > split_seq[i]) {
return; //Произведения внутри тома не по возрастанию.
}
auto temp_indexes = split_indexes;
temp_indexes.push_back(i);
split_books(split_seq, temp_indexes, split_seq[first_index]);
}
//Выход из цикла происходит только когда вся последовательность разбита на тома
if (split_pages_count > 0) {
show_seq(split_seq, split_indexes);
if (split_indexes.size() < result) {
result_seq.clear();
result_indexes.clear();
result_seq.push_back(split_seq);
result_indexes.push_back(split_indexes);
result = split_indexes.size();
} else if (result == split_indexes.size()) {
result_seq.push_back(split_seq);
result_indexes.push_back(split_indexes);
}
}
}