Помогите, пожалуйста, придумать алгоритм для решения задачи на с++

Рейтинг: -1Ответов: 2Опубликовано: 07.04.2023

Не могу понять какой алгоритм нужно использовать для решения этой задачи:

Имеется несколько романов одного писателя, для каждого из них известен объем (число страниц). Для издания сочинений романы надо сгруппировать в тома так, чтобы объем тома не превышал N страниц. Показать всевозможные варианты компоновки романов и выбрать вариант с минимальным количеством томов.

Ответы

▲ 2

Самый простой вариант: генерировать все перестановки произведений, каждую перестановку разбить на всевозможные варианты томов. Минусы: тома с разным порядком произведений считаются разными томами, одинаковые тома в разном порядке считаются разными результатами.

Вариант реализации с перестановками через алгоритмы и рекурсивным разбиением на тома:

#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);
        }
    }
}
▲ 0

введите сюда описание изображения #include #include #include

using namespace std;

int main() {
    int N; // число романов
    cin >> N;

    vector<int> count_of_lists(N); // объемы романов
    for (int i = 0; i < N; i++) {
        cin >> count_of_lists[i];
    }

    int max_lists; // максимальный объем тома
    cin >> max_lists;

    int min_tomes = N; // минимальное количество томов
    vector<int> best_var; // лучшая комбинация романов

    //НУ КОРОЧЕ ТУТ ПРОВЕРКА, ЧТО ЕСЛИ ЗАПИСАЛИ ТОМ, ТО ЧЕ ЕГО ЕЩЕ РАЗ ЗАПИСЫВАТЬ
    for (int i = 1; i < (1 << N); i++) {
        int sum_oflists = 0;
        vector<int> current_var;

        for (int j = 0; j < N; j++) {
            if (i & (1 << j)) {
                // если j-й роман включен в текущую комбинацию
                sum_oflists += count_of_lists[j];
                current_var.push_back(j);
            }
        }

        if (sum_oflists <= max_lists && (N - static_cast<int>(current_var.size())) < min_tomes) {
            // проверка если комбинцаия не заполнена
            min_tomes = N - current_var.size();
            best_var = current_var;
        }
    }

    sort(best_var.begin(), best_var.end());

    // вывод лучшей комбинации
    for (int i = 0; i < static_cast<int>(best_var.size()); i++) {
        cout << best_var[i] + 1 << " ";
    }

    return 0;
}

Тут только перебор, может и есть варианты записи размера томов в матрицу, но я не супер-крут в алгебре, поэтому решил задачу так. Если олимпиадные задачи решаете, значит побитовый сдвиг шарите.

(i & (1 << j)) - если j != 0, то после побитового i все биты заполнятся нулями кроме бита j.

(1 << N) -> (i < pow(n, 2))