找到最有价值的组合的算法?

时间:2015-06-23 09:18:12

标签: algorithm linear-programming

我正在开展一个小项目,根据用户的一些意见,我需要帮助找到最好和最便宜的门票:

  • 在哪些时段(开始和结束日期)之间?
  • 在此期间内,您是跳过1个或多个日期吗?
  • 您每天需要多少次使用机票?

有x张票。门票可以包括:

  1. 单票,只能使用一次,价格5美元。
  2. 期间门票(每天无限次骑行),可在1天/ 10美元,3天/ 30美元,7天/ 45美元内使用。
  3. 我想我正在寻找某种算法来确定基于期间(包括或不包括跳过日期)的最佳票证组合,以及它们的价格。

    另外,我想有必要考虑这样一种情况,即购买一张比我实际需要更多天票的期票会更好,更便宜,但根据我骑多少次票价会更便宜每天都要去......

    更新(基于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;
    
    }
    

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

3 个答案:

答案 0 :(得分:0)

假设您已将所有数据存储在某些天数中,并且每天都记录当天的游乐设施数量。

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

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

树解决方案:(叶子中的数据)
树选项是将其排列在树中,每个子树保存该子树的最佳解决方案。然后,每个子树根可以检查是否使用票据&#34;覆盖&#34;当考虑到遗漏部分的根的值时,只有部分子树才有用 也许排名树会在这里派上用场。

答案 1 :(得分:0)

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

首先,为了简化算法,让每个l计算最便宜的票据,可用于连续l天。{对于你的例子,这将是:1天10美元,2天30美元(购买3天票,仅使用2天),3天30美元,4-7天45美元等(显然会有一些最大价值除l之外不会有这样的票。)将这些结果表示为cost[l]

现在主要的动态编程解决方案。对于[ibegin]范围内的每个日期end,请计算ans[i] =购买门票的最低费用,以至少覆盖从begini的间隔i

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

这为您提供了N解决方案,其中L是天数,{{1}}是单个故障单的最大范围。

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

答案 2 :(得分:0)

从我的例子来看,基于@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

或者这就是你说的时候的意思&#34;(显然会有一些超过l的最大价值,没有这样的票。)&#34;?

对你的想法进行另一轮解释真是太好了!