【问题标题】:Algorithm to find the most valuable combination?找到最有价值组合的算法?
【发布时间】:2015-06-23 09:18:12
【问题描述】:

我正在做一个小项目,我需要帮助来根据用户的一些输入找到最好和最便宜的门票:

  • 在什么期间(开始日期和结束日期)?
  • 在此期间,您是否跳过 1 个或多个日期?
  • 您每天需要使用多少次票?

有x张票。一张票可以覆盖:

  1. 单程票,仅限使用一次,售价 5 美元。
  2. 期间票(每天无限次乘坐),从 1 天/10 美元、3 天/30 美元、7 天/45 美元起,随心所欲地使用。

我想我正在寻找某种算法来确定基于时段(包括或不包括跳过日期)以及它们的价格的最佳门票组合。

另外,我想有必要考虑这样一种情况,即购买一张比我实际需要的天数更多的定期票对我来说会是一个更好、更便宜的结果,但根据我乘坐的次数而定更便宜每天都去...

更新(基于 Petr 的建议..)

<?php


$tickets = array(
    array("price"=>5, "label"=>"single", "period"=>null),
    array("price"=>10, "label"=>"1 day", "period"=>1),
    array("price"=>30, "label"=>"3 days", "period"=>3),
    array("price"=>45, "label"=>"7 days", "period"=>7)
);

$trips = 2;
$startDate = new DateTime("2015-06-23");
$endDate = new DateTime("2015-06-30");
$endDate->modify("+1 day");

$interval = DateInterval::createFromDateString('1 day');
$period = new DatePeriod($startDate, $interval, $endDate);

$cost = array();
$day = 1;

foreach( $period as $date ){
    $span = $startDate->diff($date);
    $days = ( $span->format('%a') + 1 );

    $ticket = getCheapestTicket( $days );
    $cost[ $day ] = $ticket;

    $day++;
}


function getCheapestTicket( $days ){
    global $tickets, $trips;

    $lowestSum = null;
    $cheapestTicket = null;

    echo "-- getCheapestTicket --" . PHP_EOL;
    echo "DAYS TO COVER: " . $days . " / TRIPS: " . $trips . PHP_EOL;

    foreach( $tickets as $ticket ){
        $price = $ticket['price'];
        $period = $ticket['period'] ? $ticket['period'] : -1;

        if( $ticket['period'] ){
            $units = ceil( $days / $period );
            $sum = round( $units * $price );
        }else{
            $units = ceil( $days * $trips );
            $sum = round( ( $days * $price ) * $trips );
        }

        if( $sum <= $lowestSum || !$lowestSum ){

            if( $ticket['period'] > $cheapestTicket['period'] ){
                $cheapestTicket = $ticket;
                $lowestSum = $sum;
            }else{
                $lowestSum = $sum;
                $cheapestTicket = $ticket;
            }

        }

        echo "TICKET: " . $ticket['label'] . " / Units to cover days: " . $units . " / Sum: " . $sum . " / Period: " . $period . PHP_EOL;
    }

    echo "CHEAPEST TICKET: " . $cheapestTicket['label'] .
    " / PRICE PER UNIT: " . $cheapestTicket['price'] . " / SUM: " . $lowestSum . PHP_EOL. PHP_EOL;

    return $cheapestTicket;

}

我不确定这是否还在路上:)

【问题讨论】:

  • 请为您的问题提供示例输入和输出。您还应该阅读一些有关动态编程的内容 - 闻起来像是一个非常适合它的问题(尽管我不是 100% 确定,因为我没有完全理解这个问题)

标签: algorithm linear-programming


【解决方案1】:

假设您将所有数据存储在某个日期数组中,并且每天都会记下当天的乘车次数。

旁注:我将放宽持续 24 小时的票的条件,并假设每张定期票都适用于该日期(即不是从 15:00 开始,一直持续到第二天的 14:59)。这可以通过将其视为每小时时间单位而不是天来解决。

次优解:
现在让我们将当天购买一张乘车票的成本分配给所有天,然后开始迭代数组并检查是否可以用更便宜的票代替其中的一些。当没有更改完成时,您完成。这里的问题是您可能会错过最佳解决方案。您可以分配两张 3 天票(第 1-3 天和 7-9 天),其中一张 7 天票 (2-8) 和两张 1 天票会更好。

树解决方案:(叶子中的数据)
树选项是将其排列在一棵树中,每个子树都拥有该子树的最佳解决方案。然后,考虑到被遗漏部分的根值,每个子树根可以检查是否使用仅“覆盖”部分子树的票证有用。
也许在这里排名树会派上用场。

【讨论】:

  • 如果你看看我的例子,你能给出更具体的答案吗?
【解决方案2】:

您可以使用动态编程方法解决此问题。

首先,为了算法的简单性,让我们为每张l 计算最便宜的 票,该票可用于连续几天覆盖l。对于您的示例,这将是:1 天 10 美元、2 天 30 美元(购买 3 天票并仅使用 2 天)、3 天 30 美元、4-7 天 45 美元等(显然会有一些最大值l 的数量,超过此数量将没有此类票证。)将这些结果表示为 cost[l]

现在主要的动态规划解决方案。对于 [begin, end] 范围内的每个日期i,计算ans[i] = 购买门票的最低成本,以覆盖至少从begini 的区间。

假设您已经计算了日期i 之前的所有值,那么日期i 的计算就很简单了。您将需要一些在i 当天结束的票。假设它涵盖了l天的长度,那么最后一张票的价格将是cost[l],您还必须涵盖从begini-l的天数,这将花费ans[i-l](或如果i-lbegin 之前,则为零)。因此,对于给定的i,遍历所有可能的ls 并找到最小化解决方案的那个。

这为您提供了O(NL) 解决方案,其中N 是天数,L 是单个工单的最大跨度。

这一切都假设每个勾选都涵盖了几个完整的连续。如果它涵盖了 24 个完整小时(从购买时间到第二天的同一时间),那么只需计算每个小时的答案。

【讨论】:

  • 我不确定我是否跟随。请根据您的建议查看我的第一步。
【解决方案3】:

从我的例子来看,根据@Petr 所说,我真的不知道它如何解决例如该期间涵盖 8 天(每天 2 次旅行)并且您最终得到这样的结果的情况:

-- getCheapestTicket --
DAYS TO COVER: 8 / TRIPS: 2
TICKET: single / Units to cover days: 16 / Sum: 80 / Period: -1
TICKET: 1 day / Units to cover days: 8 / Sum: 80 / Period: 1
TICKET: 3 days / Units to cover days: 3 / Sum: 90 / Period: 3
TICKET: 7 days / Units to cover days: 2 / Sum: 90 / Period: 7
CHEAPEST TICKET: 1 day / PRICE PER UNIT: 10 / SUM: 80

它应该给我这个组合的结果:

7 days, $45
1 day, $10

或者这就是你所说的“(显然会有一些 l 的最大值,超过它就没有这样的票。)”?

如果您的想法得到另一轮解释,那真是太好了!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-26
    • 2011-12-11
    • 1970-01-01
    • 1970-01-01
    • 2019-03-03
    • 1970-01-01
    相关资源
    最近更新 更多