Algorithm 列车行程分组算法

Algorithm 列车行程分组算法,algorithm,Algorithm,想象你有一个完整的日历年在你面前。在某些情况下,您可能会在一天内乘坐几次火车,并且每次旅行可能会到达不同的地点(即,您为每次旅行支付的车票金额可能不同) 因此,您将获得如下数据: Date: 2018-01-01, Amount: $5 Date: 2018-01-01, Amount: $6 Date: 2018-01-04, Amount: $2 Date: 2018-01-06, Amount: $4 ... 现在,您必须将这些数据分组到存储桶中。一个存储桶最多可跨越31个连续日(无间隔

想象你有一个完整的日历年在你面前。在某些情况下,您可能会在一天内乘坐几次火车,并且每次旅行可能会到达不同的地点(即,您为每次旅行支付的车票金额可能不同)

因此,您将获得如下数据:

Date: 2018-01-01, Amount: $5
Date: 2018-01-01, Amount: $6
Date: 2018-01-04, Amount: $2
Date: 2018-01-06, Amount: $4
...
现在,您必须将这些数据分组到存储桶中。一个存储桶最多可跨越31个连续日(无间隔),且不能与另一个存储桶重叠

如果铲斗的列车行程少于32次,则铲斗将为蓝色。如果它有32次或更多的火车旅行,它将是红色的。这些桶还将根据票证成本的总和获得一个值

当你把所有的旅行分组后,蓝色的桶就会被扔掉。所有红色水桶的价值加起来,我们称之为奖品

目标是获得最高价值的奖品


这就是我的问题。我想不出一个好的算法来做这件事。如果有人知道解决这个问题的好方法,我愿意听听。或者,如果您知道其他任何地方可以帮助设计这样的算法。

简化挑战,但不要太多,以制作一个可以忽略的示例,可以手动解决

这有助于找到正确的问题

例如,只需10天,桶的最大长度为3:

对于构建桶并为其着色,我们只需要票证计数,这里是0、1、2、3

平均来说,我们每天需要超过一个桶,例如2-0-2是3天4张票。或者1-1-3,1-3,1-3-1,3-1-2,1-2

但我们只能选择2个红色桶:2-0-2和(1-1-3或1-3-3或3-1-2),因为1-2最终只有3张票,但我们至少需要4张(比每个桶的最大日跨度多一张票)

但是,虽然3-1-2明显比1-1-3多,但少票的价值可能更高


蓝色区域是不太有趣的区域,因为它不会根据票数自动进站。

这可以通过动态规划来解决

首先,按日期排序记录,并按顺序排序。 让
天(1)
天(2)
,…,
天(n)
成为购票的日子。 让
成本(1)
成本(2)
,…,
成本(n)
分别为门票成本

让<代码>乐趣(K)< /代码>如果我们只考虑第一个代码> k>代码>记录,将是最好的奖励。 我们的动态规划解决方案将计算

fun(0)
fun(1)
fun(2)
,…,
fun(n-1)
fun)
,使用前面的值来计算下一个值

基础:
fun(0)=0

转换: 什么是最佳的解决方案,<代码>乐趣(K)< /代码>,如果我们只考虑第一个<代码> k< /代码>记录? 有两种可能性:要么删除
k
-th记录,然后解决方案与
fun(k-1)
相同,要么
k
-th记录是存储桶的最后一条记录。 让我们考虑在循环中用<代码> k< /代码>记录结束的所有可能的桶,如下所述。 查看记录
k
k-1
k-2
,…,直到第一条记录。 让当前索引为
i
。 如果从
i
k
的记录跨度超过
31
连续天,则中断循环。 否则,如果记录数
k-i+1
至少为
32
,我们可以解决子问题
fun(i-1)
,然后将
i
中的记录添加到
k
,获得
cost(i)+cost(i+1)+…+成本(k)
。 值
fun(k)
是这些可能性中的最大值,以及删除
k
-th记录的可能性

回答:这只是
fun(n)
,我们考虑了所有记录的情况


在伪代码中:

fun[0] = 0
for k = 1, 2, ..., n:
    fun[k] = fun[k-1]
    cost_i_to_k = 0
    for i = k, k-1, ..., 1:
        if day[k] - day[i] > 31:
            break
        cost_i_to_k += cost[i]
        if k-i+1 >= 32:
            fun[k] = max (fun[k], fun[i-1] + cost_i_to_k)
return fun[n]
fun[0] = 0
for k = 1, 2, ..., n:
    fun[k] = fun[k-1]
    cost_i_to_k = 0
    quantity_i_to_k = 0
    for i = k, k-1, ..., 1:
        if k-i+1 > 31:
            break
        cost_i_to_k += cost[i]
        quantity_i_to_k += quantity[i]
        if quantity_i_to_k >= 32:
            fun[k] = max (fun[k], fun[i-1] + cost_i_to_k)
return fun[n]

目前还不清楚是否允许我们将一天的记录拆分为不同的存储桶。 如果答案是否定的,我们将不得不强制执行它,不考虑在一天内开始或结束记录之间的桶。 从技术上讲,它可以通过几个
if
语句来完成。 另一种方法是考虑“天”而不是“记录”,而不是“<代码>日期>代码>和<代码>费用 >,我们将工作几天。 每天将有
成本
,当天的总门票成本,以及
数量
,门票数量


编辑:根据评论,我们确实不能分割任何一天。 然后,在进行一些预处理以获取天数记录而不是票证记录之后,我们可以按如下方式进行操作,使用伪代码:

fun[0] = 0
for k = 1, 2, ..., n:
    fun[k] = fun[k-1]
    cost_i_to_k = 0
    for i = k, k-1, ..., 1:
        if day[k] - day[i] > 31:
            break
        cost_i_to_k += cost[i]
        if k-i+1 >= 32:
            fun[k] = max (fun[k], fun[i-1] + cost_i_to_k)
return fun[n]
fun[0] = 0
for k = 1, 2, ..., n:
    fun[k] = fun[k-1]
    cost_i_to_k = 0
    quantity_i_to_k = 0
    for i = k, k-1, ..., 1:
        if k-i+1 > 31:
            break
        cost_i_to_k += cost[i]
        quantity_i_to_k += quantity[i]
        if quantity_i_to_k >= 32:
            fun[k] = max (fun[k], fun[i-1] + cost_i_to_k)
return fun[n]
这里,
i
k
是天数。 请注意,我们考虑范围内的所有可能的日期:如果没有特定日期的票,我们只使用零值作为其<代码>成本<代码>和<代码>数量< /代码>值。


Edit2: 上面允许我们计算最大的总奖金,但是我们到达那里的桶的实际配置如何呢? 一般的方法是回溯:在位置
k
,我们想知道我们是如何得到
fun(k)
,如果最佳方法是跳过
k
-第条记录,或者从
k
i-1
,因为
i
方程
fun[k]=fun[i-1]+成本对成本
有效。 我们继续,直到
i
降至零

两种常用的实现方法之一是存储
par(k)
,一个“父级”,以及
fun(k)
,它对我们如何获得最大值进行编码。 例如,如果
par(k)=-1
,则最优解跳过
k
-条记录。 否则,我们将最优索引
i
存储在
par(k)
中,因此