Охранники и спрос
У вас есть N охранников в линии каждый с требованием монет. Вы можете не платить охраннику только в том случае, если его требование меньше, чем вы полностью заплатили, прежде чем добраться до него. Найдите наименьшее количество монет, которое вы потратите, чтобы пересечь всех охранников.
Я думаю, что это проблема DP, но не могу придумать формулу. Другим подходом был бы бинарный поиск ответа, но как я могу проверить, является ли количество монет возможным ответом?
3 ответа:
Это действительно проблема динамического программирования.
Рассмотрим функцию
f(i, j)
, которая являетсяtrue
(одна), если есть назначение первыхi
охранников, которые дают вам стоимостьj
. Вы можете расположить функциюf(i, j)
в таблице размераn x S
, гдеS
- сумма всех требований охранников.Обозначим
d_i
как требование охраныi
. Вы можете легко вычислить столбецf(i+1)
, Если у вас естьf(i)
, просто сканируяf(i)
и назначаяf(i+1, j + d_i)
как один, еслиf(i + 1, j)
истинно иj < d_i
, илиf(i + 1, j)
, Еслиj >= d_i
.Это работает в
O(nS)
времени иO(S)
пространстве (вам нужно только сохранить два столбца за время), которое является только псевдополиномиальным (и квадратичным, если требования каким-то образом ограничены и не растут сn
).Распространенный трюк для уменьшения сложности задачи DP состоит в том, чтобы получить верхнюю границу
Получается, что в нашем случаеB
значения оптимального решения. Таким образом, вы можете обрезать ненужные строки, получив временную сложностьO(nB)
(ну, дажеS
является верхний предел, но очень наивный).B = 2M
, гдеM
- максимальная потребность охранника. В самом деле, рассмотрим функциюbest_assignment(i)
, которая дает вам минимальное количество монет для прохождения первойi
Стражи. Пустьj
будет стражем с требованиемM
. Еслиbest_assignment(j - 1) > M
, то очевидно, что лучшее назначение для всей последовательности-заплатить охранникам за лучшее назначение первыхj-1
охранников и пропустить остальных, в противном случае верхняя граница задаетсяbest_assignment(j - 1) + M < 2M
. Но сколько может быть в первом случае? Это не может быть больше, чем2M
. Это может быть доказано противоречием. Предположим, чтоbest_assignment(j - 1) > 2M
. В этом задании охранникj-1
оплачивается? Нет, потому что2M - d_{j-1} > d_{j-1}
, таким образом, он не должен быть оплачен. Тот же аргумент справедлив и дляj-2
,j-3
, ... Таким образом, охрана не оплачивается, что абсурдно, если толькоM = 0
(очень наивный случай, который нужно проверить).Поскольку верхняя граница доказана как
2M
, DP иллюстрируется выше столбцамиn
и2M
rows решает проблему, с временной сложностьюO(nM)
и пространственной сложностьюO(M)
.
function crossCost(amtPaidAlready, curIdx, demands){ //base case: we are at the end of the line if (curIdx >= demands.size()){ return amtPaidAlready; } costIfWePay = crossCost(amtPaidAlready + demands[curIdx], curIdx+1, demands); //can we skip paying the guard? if (demands[curIdx] < amtPaidAlready){ costIfWeDontPay = crossCost(amtPaidAlready, curIdx+1, demands); return min(costIfWePay, costIfWeDontPay); } //can't skip paying else{ return costIfWePay; } }
Это выполняется за O (2^N) время, потому что он может вызвать себя дважды за выполнение. Это хороший кандидат для запоминания , потому что это чистая функция без побочных эффектов.
Вот мой подход:
int guards[N]; int minSpent; void func(int pos, int current_spent){ if(pos > N) return; if(pos == N && current_spent < minSpent){ minSpent = current_spent; return; } if(guards[pos] < current_spent) // If current guard can be skipped func(pos+1,current_spent); // just skip it to the next guard func(pos+1,current_spent+guards[pos]); // In either cases try taking the current guard }
Используется следующим образом:
minSpent = MAX_NUM; func(1,guards[0]);
Это будет пробовать все возможности его O (2^N), надеюсь, это поможет.