ДИНАМИЧЕСКОЕ ПРОГРАММИРОВАНИЕ
Метод динамического программирования состоит в разбиении задачи на подзадачи, решении их и объединении результатов. В отличии от метода «разделяй и властвуй», динамическое программирование используется, когда подзадачи не являются независимыми. Чтобы не решать некоторые подзадачи несколько раз (как это делалось бы при использовании стратегии «разделяй и властвуй»), при динамическом программировании подзадачи решаются один раз и результат решения заносится в некоторую таблицу.
Решение задачи методом динамического программирования как правило требует наличия рекуррентного соотношения между подзадачами. Скорость решения задач этим методом как правило полиномиальна, в отличии от методов полного перебора или бектрекинга.
1. Монеты. Имеются монеты достоинством v1, v2, …, vn копеек. Необходимо найти наименьшее количество монет, которыми можно выдать сумму S.
2. Задача о загрузке. Меется судно грузоподъемностью w и n предметов. Известно, что i - ый предмет имеет вес wi и ценность ci. Необходимо загрузить судно предметами так, чтобы получить максимальную прибыль.
Вход. Первая строка содержит количество предметов n и грузоподъемность судна w. Следующие n строк содержат характеристики грузов: в i - ой строке находится вес wi и ценность ci i - го груза.
Выход. Максимальная суммарная ценность грузов, которыми можно загрузить судно.
-
Пример входа
|
Пример выхода
|
4 9
|
21
|
2 5
|
|
2 5
|
|
4 9
|
|
1 2
|
|
3. Доллары [Вальядолид, 147, 357, 674]. Валюта Новой Зеландии состоит из купюр 100$, 50$, 20$, 10$, 5$ и монет 2$, 1$, 50c, 20c, 10c, 5c. Необходимо определить количество способов, которыми можно составить заданную сумму. Например, 20 центов можно представить четырьмя способами: 20, 10 + 10, 10 + 5 + 5, 5 + 5 + 5 + 5.
Вход. Состоит из нескольких действительных чисел, представляющих собой денежную сумму в долларах. Каждая входная сумма не более 300.00$ и кратна 5 центам. Последний тест содержит сумму 0.00 и не обрабатывается.
Выход. Для каждого теста вывести денежную сумму, выровненную справа в поле длины 6, и число способов ее представления, выровненную справа в поле длины 17.
-
Пример входа
|
Пример выхода
|
0.20
|
0.20 4
|
2.00
|
2.00 293
|
0.00
|
|
4. Оптимальное умножение матриц [Вальядолид, 348]. Для умножения матрицы А размера x y на матрицу В размера y z следует выполнить x * y * z операций. Необходимо вычислить произведение матриц A1 * A2 * … * An, сделав при этом минимальное количество операций умножения.
Рассмотрим пример. Пусть имеются три матрицы со следующими размерами: X – 5 10, Y – 10 20, Z – 20 35.
Перемножим матрицы в последовательности ((X Y) Z):
-
XY: 5 * 10 * 20 = 1000 операций, получим при этом матрицу размера 5 20;
-
((X Y) Z): 5 * 20 * 35 = 3500 операций;
-
Общее число умножений равно 4500.
Перемножим матрицы в последовательности (X (YZ)):
-
YZ: 10 * 20 * 35 = 7000 операций, получим при этом матрицу размера 10 35;
-
(X (YZ)): 5 * 10 * 35 = 1750 операций;
-
Общее число умножений равно 8750.
Таким образом, для минимизации числа умножений следует перемножить матрицы в последовательности ((X Y) Z).
Вход. Первая строка каждого теста содержит количество перемножаемых матриц n (n 10). Далее следуют n строк, каждая из которых содержит два числа – размер матрицы Ai (1 i n).
Выход. Следует перемножить матрицы A1 * A2 * …* An., произведя наименьшее количество умножений. Оптимальный порядок перемножения матриц вывести согласно формату, приведенному ниже.
-
Пример входа
|
Пример выхода
|
3
|
Case 1: (A1 x (A2 x A3))
|
1 5
|
Case 2: ((A1 x A2) x A3)
|
5 20
|
Case 3: ((A1 x (A2 x A3)) x ((A4 x A5) x A6))
|
20 1
|
|
3
|
|
5 10
|
|
10 20
|
|
20 35
|
|
6
|
|
30 35
|
|
35 15
|
|
15 5
|
|
5 10
|
|
10 20
|
|
20 25
|
|
0
|
|
5. Наибольшая общая подпоследовательность [Вальядолид, 10405]. По заданным двум символьным последовательностям найти длину наибольшей общей подпоследовательности. Например, длина наибольшей общей подпоследовательности последовательностей abcdgh и aedfhr равна 3.
Вход. Состоит из пар строк. Первая строка содержит первую последовательность, а вторая строка – вторую последовательность. Каждая входная последовательность содержит не более 1000 символов.
Выход. Для каждого теста вывести в отдельной строке длину наибольшей общей подпоследовательности.
-
Пример входа
|
Пример выхода
|
a1b2c3d4e
|
4
|
zz1yy2xx3ww4vv
|
3
|
abcdgh
|
26
|
aedfhr
|
14
|
abcdefghijklmnopqrstuvwxyz
|
|
a0b0c0d0e0f0g0h0i0j0k0l0m0n0o0p0q0r0s0t0u0v0w0x0y0z0
|
|
abcdefghijklmnzyxwvutsrqpo
|
|
opqrstuvwxyzabcdefghijklmn
|
|
6. Путешествие Теобальдо [Вальядолид, 10681]. Теобальдо необходимо совершить переход из города s в город e, затратив на него не более чем d дней. Так как Теобальдо ленивый, он хочет потратить на путешествие максимально возможное число дней. Каждый следующий день Теобальдо должен переходить из одного города в другой. В задаче требуется узнать, сможет ли Теобальдо выполнить переход.
Вход. Состоит из нескольких тестов. Первая строка каждого теста содержит количество городов c (0 < c 100) и количество дорог l (0 l 500) между городами. Каждая из следующих l строк содержит номера двух городов A и B (1 a, b c, a b), соединенных дорогой. Далее следует строка, содержащая номер начального города s, номер конечного города e и максимальное число дней d (0 d 200), отведенное на путешествие. Последний тест содержит c = l = 0 и не обрабатывается.
Выход. Для каждого теста вывести сообщение о том, сможет ли Теобальдо совершить переход, в соответствии с ниже приведенным форматом.
-
Пример входа
|
Пример выхода
|
3 2
|
Yes, Teobaldo can travel.
|
1 2
|
No, Teobaldo can not travel.
|
2 3
|
|
3 1 2
|
|
|
|
3 2
|
|
1 2
|
|
1 3
|
|
1 3 2
|
|
|
|
0 0
|
|
7. Путешествующий торговец [Вальядолид, 10702]. Джин – путешествующий торговец. Он ходит по городам, продавая и покупая товары. Страна, по которой он ходит, состоит из С городов. Начиная свой путь из города S, Джин должен сделать T переходов между городами. Торговцу разрешается посещать города, в которых он уже был. Свой путь Джин должен завершить в одном из E городов. Известна прибыль, которую Джин получает, совершив переход из i - го города в j - ый. Необходимо вычислить максимальную прибыль, которую он может получить, пройдя весь путь.
Вход. Состоит из нескольких тестов. Первая строка каждого теста содержит четыре целых числа: количество городов C (2 С 100), номер начального города S (1 S 100), число финальных городов E (1 E 100) и количество переходов T (1 T 1000). Следующие С строк содержат по С чисел. j - ое число i – ой строки содержит прибыль, которую получит Джин совершив переход из i - го города в j – ый. Поскольку Джин не может идти из i - ого города в i - ый, то i - ое число i – ой строки содержит 0. Далее в одной строке следуют E номеров городов, в которых торговец может завершить свой путь. Последний тест содержит C = S = E = T = 0 и не обрабатывается.
Выход. Для каждого теста вывести максимальную прибыль, которую Джин может получить завершив свой путь.
-
Пример входа
|
Пример выхода
|
3 1 2 2
|
7
|
0 3 5
|
|
5 0 1
|
|
9 2 0
|
|
2 3
|
|
|
|
0 0 0 0
|
|
8. Трамваи в Барселоне [Вальядолид, 10767]. Трамвай должен проехать по маршруту P0, P1, …, Pn. Расстояние между остановками Pi-1 и Pi равно Si. Каждую часть пути Si водитель должен проезжать с постоянной скоростью vi, которую он выбирает на остановке Pi-1. Обозначим через Mi максимально возможную скорость, которую водитель может выбрать на остановке Pi-1 (0 < vi Mi). Вероятность поломки трамвая на промежутке Si равна vi / Mi. Если поломка имеет место на отрезке Si, то она случается как раз на середине пути, после чего в течении 10 секунд включается аварийная система и со скоростью 5 метров в секунду трамвай без поломок едет до остановки Pi.
Пусть M0 – максимально допустимая скорость трамвая, с которой он может выехать с начальной станции P0. Тогда максимальная скорость, с которой трамвай может выехать со станции Pi-1, равна Mi = M0 – Ci, где Ci – суммарное количество поломок на промежутках S1, …, Si-1.
Вычисление среднего времени покажем на примере. Пусть длина пути равна 300 метров, максимально возможная начальная скорость равна 25 метров в секунду. Если водитель выберет скорость 25 м/с, то трамвай обязательно поломается. И тогда он проедет половину пути за 150 / 25 = 6 секунд, простоит 10 секунд и доедет с аварийной скоростью 5 м/с до следующей остановки за 150 / 5 = 30 секунд. Всего при этом потратив 6 + 10 + 30 = 46 секунд. Допустим, что водитель выбирает скорость 15 метров в секунду. С вероятностью 15 / 25 = 0.6 произойдет поломка. В случае поломки трамвай проедет 150 метров со скоростью 15 м/с (потратив на это 10 секунд), потом 10 секунд простоит и со скоростью 5 м/с доедет до конца (150 / 5 = 30 секунд). С вероятностью 0.4 трамвай не поломается и доедет до следующей остановки за 300 / 15 = 20 секунд. Средний час равен 0.6 * 50 + 0.4 * 20 = 38 секунд.
В задаче необходимо вычислить наименьшее среднее время, за которое трамвай может проехать весь путь от остановки P0 до Pn.
Вход. Каждая строка соответствует одному тесту и содержит начальную максимально возможную скорость трамвая M0 (5 M0 25), значение n (1 n M0 - 1) а также длины всех секций S1, …, Sn.
Выход. Для каждого теста вывести оптимальное среднее время, за которое трамвай проедет весь путь. Результат выводить с 4 десятичными знаками после запятой.
-
Пример входа
|
Пример выхода
|
25 1 900
|
102.0000
|
25 2 900 900
|
205.0303
|
25 2 305.15 980.76
|
150.0000
|
5 1 1000
|
210.0000
|
9. Распределение оценок [Вальядолид, 10910]. На экзамене один студент сдавал n предметов и получил в сумме t балов. Он сдал все n предметов, для зачета каждого следовало набрать минимум p балов. Определить, сколькими способами студент мог сдать экзамен. Например, при n = 3, t = 34, p = 10 оценки по предметам могут распределиться следующим образом:
-
|
Предмет 1
|
Предмет 2
|
Предмет 3
|
1
|
14
|
10
|
10
|
2
|
13
|
11
|
10
|
3
|
13
|
10
|
11
|
4
|
12
|
11
|
11
|
5
|
12
|
10
|
12
|
6
|
11
|
11
|
12
|
7
|
11
|
10
|
13
|
8
|
10
|
11
|
13
|
9
|
10
|
10
|
14
|
10
|
11
|
12
|
11
|
11
|
10
|
12
|
12
|
12
|
12
|
12
|
10
|
13
|
10
|
13
|
11
|
14
|
11
|
13
|
10
|
15
|
10
|
14
|
10
|
Студент может сдать экзамен 15 способами.
Вход. Первая строка содержит количество тестов. Каждый тест содержит в одной строке три числа n, t, p, значения каждого из которых не больше 70.
Выход. Для каждого теста вывести количество способов, которыми студент может сдать экзамен. Ответ является знаковым 32-битовым числом.
-
Пример входа
|
Пример выхода
|
2
|
15
|
3 34 10
|
15
|
3 34 10
|
|
10. Как Вы прибавляете? [Вальядолид, 10943]. По заданному целому n установить количество его разбиений на k неотрицательных слагаемых. Например, для n = 20 и k = 2 существует 21 разбиение: 0 + 20, 1 + 19, 2 + 18, ..., 19 + 1, 20 + 0.
Вход. Каждая строка содержит два целых числа n и k (1 n, k 100). Последний тест содержит n = k = 0 и не обрабатывается.
Выход. Для каждого теста вывести количество разбиений числа n на k неотрицательных слагаемых. Ответ выводить по модулю 1000000.
-
Пример входа
|
Пример выхода
|
20 2
|
21
|
20 2
|
21
|
0 0
|
|
11. Задача группирования [Вальядолид, 11026]. Имеется n чисел. Выберем из них группу из k элементов. Две группы считаются разными, если существует хотя бы один элемент, который принадлежит одной группе и не принадлежит другой. Группирующей системой будем называть множество всех возможных групп. Например, из четырех элементов a, b, c, d можно составить шесть групп по два элемента. Группирующей системой для множества {a, b, c, d} и k = 2 будет множество {ab, ac, ad, bc, bd, cd}.
Похожестью группирующей системы называется число, равное сумме произведений всех элементов групп, взятой по модулю m. Похожесть для представленного выше примера для k = 1 равна F1 = (a + b + c + d) mod m. Для k = 2 похожесть равна F2 = (ab + ac + ad + bc + bd + cd) mod m.
Для заданного множества чисел и среди всех всевозможных k = 1, …, n найти похожесть Fk с максимальным значением.
Вход. Первая строка каждого теста содержит числа n (2 n 1000) и m (1 m 231). Следующая строка содержит n положительных целых чисел. Все числа не более 1000. Последний тест содержит n = m = 0 и не обрабатывается.
Выход. Для каждого теста вывести значение максимальной похожести среди всех возможных групп.
-
Пример входа
|
Пример выхода
|
4 10
|
5
|
1 2 3 4
|
50
|
4 100
|
5
|
1 2 3 4
|
|
4 6
|
|
1 2 3 4
|
|
0 0
|
|
12. Подсчет общих подпоследовательностей [Топкодер, раунд 298, дивизион 1, уровень 3]. Подпоследовательность образуется из строки удалением нуля или нескольких символов из нее. По заданным трем строкам следует подсчитать количество их разных непустых общих подпоследовательностей.
Класс: CountingCommonSubsequences
Метод: long long countCommonSubsequences(String a,
String b, String c)
Ограничения: a, b и c содержат от 1 до 50 символов ‘a’ – ‘z’.
Вход. Три строки a, b и c.
Выход. Количество общих подпоследовательностей трех строк a, b и c.
Пример входа
-
a
|
b
|
c
|
“call”
|
“accelerate”
|
“candle”
|
“topcoder”
|
“topcoder”
|
“topcoder”
|
“no”
|
“correct”
|
“answer”
|
Пример выхода
6
239
0
13. Исправить расстановку скобок [Топкодер, раунд 301, дивизион 2, уровень 3]. Имеется строка из скобок. Требуется изменить наименьшее количество символов так, чтобы полученная строка была правильной. Удалять или вставлять символы нельзя. Всего существует три вида скобок: (), [] и {}. Правильная строка определяется следующими правилами:
1. Пустая строка является правильной.
2. Если строка s правильная, то правильными являются также (s), [s] и {s}.
3. Если строки s и t правильные, то правильной будет строка st.
Например, строки “([{}])”,“” и “(){}[]” являются правильными, а “([}]”,“([)]” и “{” – нет.
Класс: CorrectingParenthesization
Метод: int getMinErrors(string s)
Ограничения: s содержит от 0 до 50 символов, s содержит четное количество символов ‘(’, ‘)’, ‘{’, ‘}’, ‘[’, ‘]’.
Вход. Строка символов s.
Выход. Наименьшее количество символов, которое можно изменить так, чтобы строка стала правильной.
Пример входа
-
s
|
"([{}])()[]{}"
|
"([)]"
|
"([{}[]"
|
Пример выхода
0
2
1
14. Просуммировать всех [Топкодер, раунд 311, дивизион 1, уровень 2]. Найти сумму цифр всех чисел от lowerBound до upperBound.
Класс: SumThemAll
Метод: long long getSum(int lowerBound, int upperBound)
Ограничения: 0 upperBound 2*109, 0 lowerBound upperBound.
Вход. Два целых числа lowerBound и upperBound.
Выход. Сумма цифр всех чисел от lowerBound до upperBound.
Пример входа
-
lowerBound
|
upperBound
|
0
|
3
|
14
|
53
|
24660
|
308357171
|
Пример выхода
6
296
11379854844
15. Забор для травы [Топкодер, раунд 314, дивизион 2, уровень 3; дивизион 1, уровень 2]. У Миссис Данси имеются несколько частей изгороди. Она хочет при помощи них ограничиться максимальную площадь. При этом известно, что любая замкнутая изгородь должна иметь форму треугольника, каждая сторона которого равна одной из имеющихся частей. Резать части нельзя, каждая сторона треугольника должна состоять только из одной части изгороди.
Необходимо найти максимальную площадь участка, которую можно оградить треугольными формами.
Класс: GrasslandFencer
Метод: double maximalFencedArea(vector fences)
Ограничения: fences содержит от 1 до 16 элементов, 1 fences [i] 100.
Вход. Целочисленный массив fences, содержащий длины имеющихся кусков забора.
Выход. Максимальная площадь, которую можно оградить треугольными формами.
Пример входа
-
fences
|
{3,4,5,6,7,8,9}
|
{1,2,4,8}
|
{7,4,4,4}
|
Пример выхода
36.754383146489694
0.0
6.928203230275509
16. Починка забора [Топкодер, раунд 325, дивизион 1, уровень 1]. Вам необходимо починить старый забор. Забор состоит из набора досок, некоторые из которых выломаны. Доски пронумерованы слева направо в возрастающем порядке. Починка всех досок от i - ой до j - ой (i < j) стоит . Целые доски обозначаются символом ‘.’ (точка), сломанные – ‘X’. Для уменьшения общей стоимости ремонта иногда выгодно ремонтировать даже целые доски. Необходимо найти минимальную стоимость ремонта всего забора.
Класс: FenceRepairing
Метод: double calculateCost(vector boards)
Ограничения: boards содержит от 1 до 50 элементов, boards[i] содержит от 1 до 50 символов ‘.’ И ‘X’.
Вход. Массив строк boards. Объединив их, получим состояние забора, который подлежит ремонту.
Выход. Минимальная стоимость ремонта всего забора.
Пример входа
-
boards
|
{"X.X...X.X"}
|
{"X.X.....X"}
|
{".X.......X", "..........", "...X......", "...X..X...", "..........",
"..........", "..X....XX.", ".........X", "XXX", ".XXX.....X"}
|
Пример выхода
3.0
2.732050807568877
9.591663046625438
17. Расширенное счастливое число [Топкодер, раунд 334, дивизион 2, уровень 3; дивизион 1, уровень 2]. Дано натуральное число n. Возведем в k - ую степень каждую его цифру и просуммируем полученные результаты. Обозначим результат через Sk(n). Например, S2(65) = 62 + 52 = 61. Построим последовательность n, Sk(n), Sk(Sk(n)), … . Счастьем числа n по отношению к k будем называть наименьшее число в этой последовательности.
По заданным числам a, b и k следует найти счастье каждого числа между a и b включительно по отношению к k и вернуть их сумму.
Класс: ExtendedHappyNumbers
Метод: long long calcTheSum(int a, int b, int k)
Ограничения: 1 a, b 106, 1 k 6.
Вход. Три числа a, b, k.
Выход. Найти счастье каждого числа между a и b включительно по отношению к k и вернуть их сумму.
Пример входа
-
a
|
b
|
k
|
13
|
13
|
2
|
1
|
5
|
2
|
535
|
538
|
3
|
Пример выхода
1
14
820
18. Быстрый почтальон [Топкодер, раунд 346, дивизион 2, уровень 3]. Почтальон должен разнести письма по домам, расположенным на одной улице. Адреса представляются собой расстояния от левого конца улицы до домов. Известно граничное время, в течение которого следует доставить каждое письмо в каждый дом. Скорость почтальона 1 метр в секунду, доставка писем производится моментально, как только почтальон поравняется с домом. Необходимо определить, сможет ли почтальон доставить все письма. В случае утвердительного ответа следует найти наименьшее время, за которое он сможет это сделать.
Класс: FastPostman
Метод: int minTime(vector address, vector maxTime,
int initialPos)
Ограничения: address и maxTime содержат одинаковое количество элементов, не большее 50, 1 address[i], initialPos 106, 1 maxTime[i] 109, 1 maxTime[i] 109.
Вход. Массив адресов домов address и массив maxTime, содержащий граничное время разноса соответствующих писем. Переменная initialPos содержит начальное положение почтальона.
Выход. Наименьшее время, за которое почтальон сможет разнести все письма по указанным адресам с учетом граничного времени. Если он не сможет это сделать, то вернуть -1.
Пример входа
-
address
|
maxTime
|
initialPos
|
{1,3,5,7}
|
{9,2,5,100}
|
4
|
{1,5}
|
{6,2}
|
3
|
{1000000,1000000,1000000,1000000}
|
{563,23452,32426,1}
|
1000000
|
{1000000}
|
{45634}
|
876
|
Пример выхода
13
6
0
-1
19. Прыгатель по платформам [Топкодер, раунд 353, дивизион 2, уровень 3; дивизион 1, уровень 2]. В старой аркадной игре Вы попали на бонусный уровень. Имеется набор платформ, на которых находятся монеты. Вы должны прыгать с одной платформы на другую, собирая деньги. С каждой платформы можно прыгать только на платформу, находящуюся ниже. Начальной можно выбрать любую платформу.
Опишем поведение игрока при прыжках. При каждом прыжке горизонтальная составляющая скорости является константой, не превышающей v. Движение вниз происходит по законам физики: ускорение свободного падения равно g. Начальная скорость игрока равна 0.
Каждая платформа является точкой и имеет формат “X Y C”, где X и Y – координаты платформы, а C – количество монет на ней. Большие значения координаты Y имеют платформы, находящиеся выше. Определить наибольшее количество монет, которое может собрать игрок.
Класс: PlatformJumper
Метод: int maxCoins(vector platforms, int v, int g)
Ограничения: platforms содержит от 1 до 50 элементов, platforms[i] имеет формат “X Y C”, 0 X, Y 5000, 0 C 10000, 1 g, v 100.
Вход. Информация о платформах platforms, наибольшая возможная горизонтальная составляющая скорости игрока v и ускорение свободного падения g.
Выход. Наибольшее количество монет, которое может собрать игрок.
Пример входа
-
platforms
|
v
|
g
|
{"3 10 7", "5 15 7", "8 9 12" }
|
2
|
10
|
{"0 0 1", "2 4 1", "4 0 1"}
|
1
|
2
|
{"0 0 1", "5000 5000 10"}
|
100
|
87
|
Пример выхода
14
2
10
УКАЗАНИЯ И РЕШЕНИЯ
1. Монеты. Рассмотрим подзадачу, в которой требуется найти наименьшее количество монет, которыми можно выдать сумму i , i S. Обозначим решение этой подзадачи через f(i). Если известны все значения f(j), 1 j i, то легко найти f(i + 1):
f(i + 1) = f(i + 1 – vj) + 1, i + 1 vj
По умолчанию положим f(0) = 0, так как сумму в 0 копеек можно выдать 0 монетами. Значения решений для подзадач f(i) храним в некотором числовом массиве m.
Пример. Пусть имеются монеты достоинством 1, 2 и 5 копеек. Значение искомой суммы равно S = 9. Значение элементов массива m имеет вид:
-
i
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
9
|
f(i)
|
0
|
1
|
1
|
2
|
2
|
1
|
2
|
2
|
3
|
3
|
Сумму в 9 копеек можно выдать так: 9 = 5 + 2 + 2.
2. Задача о загрузке. Обозначим через f(i, w) максимальную стоимость, которую можно получить имея лишь первые i грузов и грузоподъемность w. Если i - ый груз не использовался при подсчете f(i, w), то f(i, w) = f(i – 1, w). Иначе значение f(i, w) равно f(i – 1, w - wi) + ci. Отсюда получаем рекуррентное соотношение:
f(i, w) = max( f(i – 1, w), f(i – 1, w - wi) + ci), i > 1
f(1, wi) = ci
Пример. Рассмотрим тест, в котором имеются четыре груза со следующими характеристиками:
вес, wi
|
2
|
2
|
4
|
1
|
стоимость, ci
|
5
|
5
|
9
|
2
|
Положим изначально m[0] = 0, m[i] = -1, i = 1, …, 9. Значения f(i, w) занесем в следующую таблицу:
i \ w
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
9
|
1
|
0
|
-1
|
5
|
-1
|
-1
|
-1
|
-1
|
-1
|
-1
|
-1
|
2
|
0
|
-1
|
5
|
-1
|
10
|
-1
|
-1
|
-1
|
-1
|
-1
|
3
|
0
|
-1
|
5
|
-1
|
10
|
-1
|
14
|
-1
|
19
|
-1
|
4
|
0
|
2
|
5
|
7
|
10
|
12
|
14
|
16
|
19
|
21
|
Реализация. Положим MAX = w + 1. Положим m[0] = 0, m[i] = -1, i = 1, …, MAX – 1. Для каждого груза весом weight и стоимостью cost пересчитываем значения элементов массива m начиная с конца.
#include
#include
#define MAX 10
int i,j,n,w;
int weight,cost,m[MAX];
void main(void)
{
memset(m,-1,sizeof(m));m[0] = 0;
scanf("%d %d",&n,&w);
for(i=0;i
{
scanf("%d %d",&weight,&cost);
for(j = MAX - 1; j >= weight; j--)
if ((m[j - weight] >= 0) && (m[j] < m[j - weight] + cost))
m[j] = m[j - weight] + cost;
}
printf("%d\n",m[w]);
}
3. Доллары [Вальядолид, 147, 357, 674]. Обозначим через f(k, n) количество способов составить сумму n из первых k номиналов монет. Оно равно количеству способов составить сумму n первыми (k – 1) номиналами плюс количество способов составить сумму (n – ak) k номиналами. Имеем соотношение:
f(k, n) = f(k – 1, n) + f(k, n – ak)
-
k \ n
|
|
|
|
|
|
|
|
|
|
f[k - 1, n]
|
|
|
f[k, n – ak]
|
|
|
f[k, n]
|
|
|
|
|
|
|
|
Изначально положим f(0, 0) = 1, f(n, 0) = 0, n > 0.
Достарыңызбен бөлісу: |