我想从一系列日子中找到“桥梁日”。清单:
days = [
%{value: ~D[2017-04-01], categories: ["weekend"]},
%{value: ~D[2017-04-02], categories: ["weekend"]},
%{value: ~D[2017-04-03], categories: []},
%{value: ~D[2017-04-04], categories: []},
...
%{value: ~D[2017-04-13], categories: ["bank holiday"]},
%{value: ~D[2017-04-14], categories: ["bank holiday"]},
%{value: ~D[2017-04-15], categories: ["weekend"]},
%{value: ~D[2017-04-16], categories: ["weekend", "bank holiday"]},
... ]
在网页上呈现的月份:
有人想要最大化他/她的休假日,将在10日,11日和12日休假,因为这将导致10天假期(8日至17日),投资只有3个休假日。
我想编写一个函数bridge_days(days, number_of_invested_vacation_days)
,当使用[~D[2017-04-10], ~D[2017-04-11], ~D[2017-04-12]]
调用时,会生成这三天bridge_days(days, 3)
的列表。 3是投资假期的数量。
另一个月的例子:
bridge_days(days, 1)
会产生[~D[2017-05-26]]
,因为1个休假日的投资会带来4天的休假。
实际上bridge_days/2
通常会产生一个列表列表,因为很多时候会有多个选项。
我的方法是遍历列表,比较每天的+1和-1。问题在于它需要永远这样做。
有没有比使用这种暴力解决这个问题更聪明的方法?
答案 0 :(得分:1)
您已请求了算法。我认为关键是要把你的日子折成一份“值班/休假”的清单。天。对于四月,这将对应于:
collapsed_days = [
{:off, 2},
{:on, 5}, {:off, 2},
{:on, 3}, {:off, 5},
{:on, 4}, {:off, 2},
{:on, 5}, {:off, 2}
]
现在您的function
桥天数如下:
bridge_days(collapsed_days, 3)
在此function
中,您可以执行以下操作:
{:on, 3}
{:off, 3}
折叠生成的连续{:off, _}
天,列表变为:
[ {:off,2}, {:on,5},{:off,10}, {:on,4},{:off,2}, {:on,5},{:off,2} ]
如果您有更多的休假日申请,请相应地标记它们。例如bridge_days(collapsed_days, 5)
,除了{:off, 10}
之外,您还需要在{:off, 2}
之前或之后立即创建{:off, 10}
。
如果您的休假天数少于最小{:on, _}
金额,则尽可能多地申请。
我相信这应该总能带来最佳的解决方案,但是我可能错了,因为我没有在数学上证明这是事实。如果你不能看到如何在数学上证明这一点,那么试着通过测试所有边缘情况来证明它已经耗尽。
由于您需要返回完全dates
而不是简单的数字,因此最好使用三元组:{:off, 2, [~D[2017-04-01], ~D[2017-04-02]]}
,并相应地合并您的列表。
如果您发现会破坏此方法的边缘案例,请在评论中提供反馈。
答案 1 :(得分:1)
这是一个强力O(n^2)
算法的想法,对于短列表应该相当快。我将使用日期的简化表示:布尔列表。 true
表示这是假期,false
表示不是假期。以下是算法的工作原理:
我们将列表的每个索引视为可能的开始日。
对于每个指数,我们都会找到在给定假期数下我们可以拥有的天数。为此,我们将以前的日期分开,然后使用reduce_while
,继续花几天时间,直到我们使用了所有假期(或列表结束)。我们还跟踪遇到的假期数量。
我们使用Enum.max_by
每天应用上述步骤,并回到开始假期的最佳日子。你可以通过迭代这个索引来获得实际的休假日。
defmodule A do
def go(days, vacation) do
0..(length(days) - 1)
|> Enum.max_by(fn from ->
days |> Enum.drop(from) |> Enum.reduce_while({0, 0}, fn holiday?, {collected, taken} ->
cond do
# If it's a holiday, we've collected one day without consuming more vacations.
holiday? -> {:cont, {collected + 1, taken}}
# If we've taken all vacations possible, we halt.
vacation == taken -> {:halt, {collected, taken}}
# Otherwise, we collect another day and also consume one vacation day.
true -> {:cont, {collected + 1, taken + 1}}
end
end)
|> elem(0)
end)
end
end
t = true
f = false
# April from your screenshot.
days = [t, t, f, f, f, f, f, t, t, f, f, f, t, t, t, t, t, f, f, f, f, t, t, f, f, f, f, f, t, t]
IO.inspect A.go(days, 3)
输出:
7
因此,本月的第8天是开始度假的最佳日子,与您的描述相符。