Как мне решить это быстрее и точнее?

Вот вопрос:

Раму был ленивым фермером. Он унаследовал от отца довольно большую ферму и хороший дом. Раму сдал ферму в аренду другим и получил довольно приличный доход. Его отец держал дома буйвола и продавал его молоко, но буйвол умер через несколько дней после отца.
Раму тоже хотел заработать на буйволах, но совсем другим способом. Он решил, что его будущее заключается в спекуляции на буйволах. На рынке в его деревне каждый день покупали и продавали буйволов. Цена колебалась в течение года, но в любой отдельный день цена всегда была одной и той же.
Он решил, что будет покупать буйволов, когда цена будет низкой, и продавать их, когда цена будет высокой, и в процессе накапливать большое богатство. К сожалению, в его доме было место только для одного буйвола, поэтому он мог владеть не более чем одним буйволом в любое время.
Прежде чем выйти на рынок буйволов, он решил изучить колебания цен на буйволов за последние несколько дней и определите максимальную прибыль, которую он мог бы получить. Предположим, цена буйвола за последние 10 дней колебалась как

10 12 8 11 11 10 12 15 13 10

Раму — ленивый парень, и он подсчитал, что за последние 10 дней он был бы готов посетить рынок не более 5 раз (каждый раз, чтобы купить или продать буйвола). При этом максимальная прибыль, которую он мог бы получить, составляет 9 рупий. Для этого он покупает буйвола в 1-й день, продает его во 2-й день, покупает еще одного в 3-й день и продает его в 8-й день. мог бы заработать больше денег. Он мог купить в 1-й день, продать во 2-й день, купить в 3-й день, продать в 4-й день, купить в 6-й день и продать в 8-й день, чтобы получить прибыль в размере 10 рупий.
Ваша задача - помочь Раму рассчитать максимальная сумма, которую он может заработать, спекулируя на буйволах, с учетом истории ежедневных цен на буйволов за период и ограничения на то, сколько раз Раму готов выйти на рынок в течение этого периода.

Формат ввода
Первая строка ввода содержит два целых числа N и K, где N — количество дней, за которые доступны данные о ценах, а K — максимальное количество раз, которое Раму готов посетить рынок крупного рогатого скота. . Следующие N строк (строка 2, 3,...,N+1) содержат по одному натуральному числу в каждой. Целое число в строке i+1, 1 ≤ i ≤ N, указывает цену буйвола в день i.

Выходной формат
Одно неотрицательное целое число, указывающее максимальную сумму прибыли, которую может получить Раму, если он совершит не более K поездок на рынок.
Можно предположить, что N ≤ 400 и K ≤ 400.< br/> Лимит времени = 3сек.

Если бы у нас не было ограничения k, мы могли бы решить эту проблему, используя жадный подход,

 while c < N
   i = c
   while price[day i] > price[day i+1] increment i;
   j = i+1
   while price[day j] < price[day j+1] increment j;
   add price[day j] - price[day i] to out profit
   c = j+1

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

 //Store all the pairs generated in the previous algorithm in a linked list L, each
 //element has two attributes buy,sell
 while length of L > k/2
       find an element i in the list such the (L[i].sell- L[i-1].buy) - 
       (L[i-1].buy - L[i-1].sell) - (L[i].buy - L[i].sell) is maximized.
       Then set L[i-1].sell to L[i].sell and delete i from the list

Это проблема онлайн-судьи, и когда я представил ее, у меня было превышено ограничение по времени для некоторых тестовых случаев и неправильный ответ для некоторых и правильный только для одного тестового примера.

Что не так в моем методе и почему он может быть медленным, потому что он занимает около O(NK) времени, где N и K ‹ 400.
Как я могу улучшить свой алгоритм, чтобы решить задачу правильно?

EDIT: это не домашнее задание, я нашел эту проблему здесь: http://opc.iarcs.org.in/index.php/problems/BUFFALOES


person 2147483647    schedule 04.01.2013    source источник
comment
@niklas-r, это не домашнее задание, я прочитал этот вопрос в онлайн-судье здесь: opc.iarcs.org.in/index.php/problems/BUFFALOES   -  person 2147483647    schedule 04.01.2013
comment
@NiklasR «домашнее задание» — это устаревший тег, и даже если бы это было не так, вам нечего его добавлять.   -  person Jim Balter    schedule 04.01.2013
comment
Подсказка: лучшее решение заняло всего 1 мс   -  person Jim Balter    schedule 04.01.2013


Ответы (1)


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

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

Если у нас есть трудолюбивый трейдер, желающий посещать рынок как можно чаще, это выглядит так, как показано на Рисунке 1 ниже, где я взял первые несколько дней вашего примера. Дуги рисуются от каждого дня к каждому позже дню на графике, и дуги взвешиваются следующим образом:

  • Последовательные дни должны иметь дугу — взвешенную либо с прибылью от покупки, а затем продажи на следующий день, либо (если такая деятельность будет убыточной) с нулевым взвешиванием
  • Непоследовательные дни нуждаются в дуге только в том случае, если прибыль положительна. (Хотя вы могли бы нарисовать дуги с нулевым весом для неприбыльных пар, я опустил их ниже, чтобы график был читаемым.)

Для создания этого графика требуется O(N^2) сравнений/вычитаний. Когда у вас есть этот график, поиск наилучшего плана для фермера эквивалентен поиску самого длинного пути (например, пути от первого дня до последнего дня с наибольшей суммой значений дуги) через график. Обычно нахождение самого длинного пути через граф является NP-полным, но в этом случае у нас есть ориентированный ациклический граф — вы можете просто инвертировать все веса ребер и найти кратчайший путь с помощью алгоритма Дейкстры за полиномиальное время.

Рисунок 1. Неленивый фермер

Чтобы справиться с ленивым фермером, вам нужно адаптировать эту структуру графа таким образом, чтобы он «подсчитывал» ненулевые дуги. Мы делаем это, увеличивая график. Намного больше. Если фермер готов совершить k поездок на рынок, у него есть минимум (k/2) пар покупки/продажи. Назовем это число X и будем рисовать узлы нашего графа каждые X+1 раз.

Каждая последующая дуга в той же строке (независимо от прибыли за этот день) имеет вес 0. Дуги положительной длины перенаправляются на строку ниже. На рис. 2 показано, как это выглядит, если фермер готов совершить 4 поездки на рынок, всего 2 возможности купить/продать. Вы также добавляете фиктивный «конечный узел», к которому можно добраться из конца каждой строки бесплатно.

Вы можете видеть, что это «подсчитывает» дуги прибыли, гарантируя, что каждая возможность получения прибыли перемещается из строки в строку, и никогда не бывает возможности использовать одну и ту же строку более одного раза. Итак, опять же, вы можете найти самый длинный путь, чтобы найти правильный ответ; и снова граф направлен ациклически, поэтому его можно преобразовать в задачу о кратчайшем пути и решить за полиномиальное время.

Плохая новость заключается в том, что количество узлов и дуг значительно увеличилось. Вместо N узлов у вас есть потенциально O (N ^ 2), если k = N. Точно так же вместо O (N ^ 2) дуг у вас есть O (N ^ 3).

Рисунок 2: Ленивый фермер

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

person Novak    schedule 05.01.2013
comment
(В качестве побочного комментария этот вопрос был помечен как динамическое программирование, и, насколько мне известно, алгоритм кратчайшего пути Дейкстры квалифицируется как динамическое программирование.) - person Novak; 06.01.2013
comment
Большое спасибо за ваш ответ, я также нашел другой алгоритм, похожий на рекурсию, но он устраняет некоторые из его возможностей, и я, наконец, принял его. - person 2147483647; 06.01.2013
comment
Не поделитесь алгоритмом? - person Novak; 07.01.2013