найти заданную сумму

ceylor

задачка тут попалась, думаю как бы ее решить красиво, пока идей мало..
суть есть ряд чисел, нужно найти любую комбинацию дающую заданную сумму
пусть чисел 250. сумма нужна 100000
все они вида 3000,07 2334,88 20000,90 и т.п.
заранее спасибо откликнувшимся

smit1

Динамическое программирование

SPARTAK3959

Создаешь массив из 10млн. флагов (по одному для каждого из значений суммы) и проходишься по нему 250 раз. Можно исхитриться использовать битовые маски для ускорения.

agaaaa

Что-то я не понял, как это решает задачу.

katrin2201

Жадный алгоритм (субоптимален)

danilov

Ну, если просто флаг, то оно решает задачу, можно ли такую сумму составить, а если надо точную комбинацию, то надо в каждой ячейке держать 250 флагов, ну, можно 249=).

agaaaa

И как быстро это решит задачу для числа 0x100000000000?

Serab

Автору топика надо было всего лишь для 0x186A0

lubanj

Ну, если просто флаг, то оно решает задачу, можно ли такую сумму составить, а если надо точную комбинацию, то надо в каждой ячейке держать 250 флагов, ну, можно 249=).
а не достаточно ли в каждой ячейке просто ссылку на предка хранить?

Vadim69

может, как-то так:
подготовка:
1. упорядочиваем массив по возрастанию.
2. идя с конца, набираем сумму, минимально большую требуемой, запоминаем индексы отобранных элементов.
далее, основной участок:
пусть есть какие-то уже отобранные индексы и известна сумма элементов массива по этим индексам.
1. проверяем для каждого из них, не уменьшится ли дельта между текущей суммой и требуемой, если элемент суммы поменять на соседний с ним в массиве. если уменьшится - меняем.
2. для каждого индекса смотрим, нельзя ли его заменть на два индекса так, чтобы получившаяся сумма отличалась от требуемой меньше, чем текущая. если можно, заменяем. опять же, пользуемся упорядоченностью массива чтобы уменьшить кол-во вариантов замены.
3. для каждых двух индексов смотрим, нельзя ли их заменить на один с тем же условием что и в 2. если можно, меняем.
если хотя бы в одном пункте из 1-3 было уменьшение разницы, повторяем их. если нет - значит, конец.

lubanj

буду краток. херня

danilov

Достаточно. Можно даже не хранить массив, а держать только частичные суммы (резко сократится перебор, особенно при разреженных суммах, но будут накладки на доступ к элементам). Просто изначально были флаги, а размножить их - первое, что пришло на ум

yroslavasako

предлагаю скомбинировать все указанные идеи.
0) предобработка - выбрасываем A[i] > Sum
1) Динамическое программирование. (оформляем поиск точного решения для некоторого интервала (0<x<N
Изначально достижим ноль. Добавляем в список (сортированный - значит дерево, но контейнер не важен) достижимые суммы. Берём меньшую, снова добавляем (без повторений достижимые суммы). До тех пор пока меньшее значение не выпадет за границы интервала. (отличается от классики списком достижимости вместо массивов флагов - на случай высокого разряжения
2) Не обязательно вычислять весь интервал. Можно составить интервал для a < S, и искать ответы в виде I[l]*k+I[m] - то есть поиск ответа по модулю одного из чисел. Возмоно это ускорит подбор в случае больших S (асимптотика худшего случая остаётся неизменной).

state7401281

> сумма нужна 100000
МНЕ ТОЖЕ!111

Dmitriy82

> сортированный - значит дерево
дерево не нужно, достаточно слияния сортированных списков.

Sharp

Решение с рекурсией пойдет?
Обозначим искомое число за N.
0) упорядочиваем массив по возрастанию, заодно смотрим, нет ли искомого числа среди заданных.
Пусть a_i — упорядоченный массив.
1) если самое минимальное больше, чем искомое пополам, значит сумму не получится найти.
2) проходим по массиву и для каждого индекса считаем и сохраняем сумму всех предыдущих чисел, эти суммы сохраняем. Идти можно не до конца массива, а пока сумма текущего элемента и минимального будет все еще меньше искомого. Пусть s_i — сумма a_k, для которых k<i (s_0 = 0, s_1 = a_0, s_n = a_(n-1)+s_(n-1. Так же обозначим через m такой индекс, что a_m+a_0 <= N, а a_(m+1)+a_0>N.
3) проверили, что сумма всех чисел с a_0 по a_m больше искомого. Потому что если меньше, то ничего не выйдет. Проверить надо что a_m+s_m > N.
4) если сумма текущего и минимального дала нужный результат, то выходим.
5) Если a_m и s_m в сумме дают N, тоже выходим.
6) Берем разницу (N-a_m) и пытаемся ее найти как сумму оставшихся чисел в массиве, рекурсивно вызывая эту же функцию.
7) Если нашли, то хорошо.
8) Если не нашли, то уменьшаем m на 1 и возвращаемся к пункту 3, пока m не дойдет до нуля.
По моим прикидкам получается сложность O(n^3) и максимальная глубина рекурсии n/2. Для n порядка 250, это реальные значения.

lubanj

хуета и точка тут: .

yroslavasako

, и искать ответы в виде I[l]*k+I[m] - то есть поиск ответа по модулю одного из чисел.
на практике мы заводим одно дерево.
В него пихаем два типа нодов - достижимые суммы и остатки по модулю достижимых сумм. Если при очередной вставке в дерево найдено точное совпадение нодов разного типа, то решение найдено.
Оставить комментарий
Имя или ник:
Комментарий: