подмножество, которое дает наименьшую сумму, большую или равную заданному числу

У меня есть (мульти) набор положительных чисел, например. {71.28, 82.62, 148.77, 85.05, 50.76, 103.41}.

Я хочу найти подмножество, которое дает наименьшую сумму, большую или равную заданному числу.

Например. если минимум был 270, то результат был бы {148.77, 71.28, 50.76}, что в сумме составляет 270.81.

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


person boofaz    schedule 19.05.2017    source источник
comment
Могут ли в наборе быть отрицательные числа?   -  person Jayson Boubin    schedule 20.05.2017
comment
@JaysonBoubin набор положительных чисел   -  person Bernhard Barker    schedule 20.05.2017
comment
Ой, я пропустил это. Должно быть, вечер пятницы.   -  person Jayson Boubin    schedule 20.05.2017
comment
Небольшая оптимизация для грубой силы (построение всех комбинаций) заключалась бы в том, чтобы разделить массив на две равные части, построить все комбинации отдельно (отслеживать лучшее, если сумма превышает пороговое значение), отсортировать каждую из них по сумме, иметь указатель, указывающий на первый элемент одного массива, а другой - последний элемент другого массива, в зависимости от того, с какой стороны порога вы находитесь, вы либо увеличиваете прямое движение, либо уменьшаете обратное, чтобы найти глобальную лучшую комбинацию.   -  person maraca    schedule 20.05.2017


Ответы (1)


Задача о сумме подмножества и задача о рюкзаке очень похожи по своим решениям, и вы можете использовать любую из них для решения проблемы. Однако задача о рюкзаке имеет решение динамического программирования, которое немного больше подходит для решения этой конкретной проблемы. Взгляните на эту ссылку, чтобы увидеть мою отправную точку: http://www.geeksforgeeks.org/dynamic-programming-set-10-0-1-knapsack-problem/ Приведенное выше решение повторяет каждую перестановку набора рекурсивно, вычитая значение каждого элемента набора из начальная сумма до тех пор, пока вычитание не приведет к отрицательному значению суммы. Это представляет ситуацию, когда исследуемое подмножество имеет значение больше указанного входного числа, или в вашем примере ситуацию, когда подмножество имеет аддитивное значение больше 270. В решении DP-ранца мы просто пропускаем этот элемент набора и перемещаем на следующий. В своем решении я проверяю, является ли значение этого решения наименьшим из наблюдаемых до сих пор значением, которое больше входного числа (270 в вашем примере). если это так, я обновляю два аргумента функции. Одним из аргументов является разница между отслеживаемой суммой и элементом исследуемого нами набора. Этот аргумент дает нам близость аддитивного значения подмножества к входному числу без необходимости вычислять аддитивное значение или запоминать исходный входной номер. Другой аргумент - это текущий набор, аддитивное значение которого ближе всего к нашему входному числу, но превышает его. В C ++ этот набор хранится в ссылке std :: vector (он также может использовать набор или мультимножество). Если нет набора, который добавляет к значению больше, чем входное число, этот алгоритм возвращает пустой вектор.

#include<iostream>
#include<vector>
#include<climits>
template<typename T>
void print(std::vector<T> v)
{
        for(auto i : v)
                std::cout<<i<<" ";
        std::cout<<std::endl;
}
template<typename T>
T closestVal(T sum, std::vector<T>& set, size_t n, std::vector<T> curSet, int& minSum, std::vector<T>& ret)
{
        if(n == 0 || sum == 0)
                return 0;
        if(set[n-1] >= sum) {
                if(sum-set[n-1] > minSum) {
                        minSum = sum-set[n-1];
                        std::vector<T> newSet = curSet;
                        newSet.push_back(set[n-1]);
                        ret = newSet;
                }
                return closestVal(sum, set, n-1, curSet, minSum, ret);
        }
        else {
                std::vector<T> newSet = curSet;
                newSet.push_back(set[n-1]);
                return std::max(
                        set[n-1] + closestVal(sum-set[n-1],set,n-1, newSet, minSum, ret),
                        closestVal(sum, set, n-1, curSet, minSum, ret)
                        );
        }
}
int main()
{
        std::vector<double> ms{71.28, 82.62,148.77, 85.05, 50.76, 103.41};

        std::vector<double> ret; //ret is empty, will be filled with the return value
        int i = INT_MIN; //i is instantiated to the smallest possible number

        closestVal(270.81, ms, ms.size(), {}, i, ret);

        print(ret);
        return 0;
}
person Jayson Boubin    schedule 20.05.2017