查找最低票价

时间:2016-11-12 03:18:18

标签: algorithm optimization dynamic-programming

查找在该月的已知日期(1 ... 30)购买旅行所需的最低票价。有三种类型的门票:1天有效期为1天,2天有效,7天有效7天,7天有效,30天有效30天,25个单位。

例如:我想在一个月的[1,4,6,7,28,30]天旅行,即每月的第1天,第4天,第6天......。如何购买门票以降低成本。

我尝试使用动态编程来解决这个问题,但解决方案并没有为我提供所有案例的正确答案。这是我在Java中的解决方案:

public class TicketsCost {
    public static void main(String args[]){
        int[] arr  =  {1,5,6,9,28,30};
        System.out.println(findMinCost(arr));
    }
    public static int findMinCost(int[] arr) {
        int[][] dp = new int[arr.length][3];
        int[] tDays = {1,7,30};
        int[] tCost = {2,7,25};

        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < 3; j++) {
                if (j==0){
                    dp[i][j]= (i+1)*tCost[j];
                }
                else{
                    int c = arr[i]-tDays[j];
                    int tempCost = tCost[j];
                    int k;
                    if (c>=arr[0] && i>0){
                        for (k = i-1; k >= 0; k--) {
                            if (arr[k]<=c){
                                c = arr[k];
                            }
                        }
                        tempCost += dp[c][j];
                        int tempCostX =  dp[i-1][j] + tCost[0];
                        tempCost = Math.min(tempCost,tempCostX);

                    }
                    dp[i][j] = Math.min(tempCost,dp[i][j-1]);
                }
            }
        }
        return dp[arr.length-1][2];
    }
}

该解决方案不适用于{1,7,8,9,10}输入,它提供10但正确答案应为9.此外,对于{1,7,8,9,10,15}它给出了13但正确的是11。 我发布了我的解决方案,不是为了我调试它,而是仅供参考。我采用了自下而上的动态编程方法来解决这个问题。这种方法是否正确?

6 个答案:

答案 0 :(得分:9)

让MC( d )表示将在第1天到 d 支付所有旅行的最低费用。所需的答案是MC(30)。

要计算MC( d ),请注意以下事项:

  • 如果当天没有旅行 d ,则MC( d )= MC( d - 1)。
    • 作为一种特殊情况,所有 d ≤0的MC( d )= 0。
  • 否则,最低费用包括以下其中一项:
    • 第一天 d 的1天通行证。在这种情况下,MC( d )= MC( d - 1)+ 2。
    • d 之后或之后结束的7天通行证。在这种情况下,MC( d )= min(MC( d - 7),MC( d - 6),...,MC( d - 1))+ 7。
      • 由于MC不减少(增加一天从不降低最低成本),这可以简化为MC( d )= MC( d - 7)+ 7.(向Ravi提示,指出这一点。)
    • 整个期间的30天通行证。在这种情况下,MC( d )= 25。

正如您所知,动态编程(自下而上递归)非常适合这种情况。

为了便于编码,我建议我们首先将天数列表转换为“这是一个旅行日吗?”的查询表:

boolean[] isDayWithTrip = new boolean[31]; // note: initializes to false
for (final int dayWithTrip : arr) {
    isDayWithTrip[dayWithTrip] = true;
}

然后我们可以创建一个数组来跟踪最低成本,并从索引0开始填充它:

int[] minCostUpThroughDay = new int[31];
minCostUpThroughDay[0] = 0; // technically redundant
for (int d = 1; d <= 30; ++d) {
    if (! isDayWithTrip[d]) {
        minCostUpThroughDay[d] = minCostUpThroughDay[d-1];
        continue;
    }

    int minCost;
    // Possibility #1: one-day pass on day d:
    minCost = minCostUpThroughDay[d-1] + 2;
    // Possibility #2: seven-day pass ending on or after day d:
    minCost =
        Math.min(minCost, minCostUpThroughDay[Math.max(0, d-7)] + 7);
    // Possibility #3: 30-day pass for the whole period:
    minCost = Math.min(minCost, 25);

    minCostUpThroughDay[d] = minCost;
}

结果是minCostUpThroughDay[30]

您可以在以下位置查看上述代码:https://ideone.com/1Xx1fd

答案 1 :(得分:1)

Python3中的一种递归解决方案。

from typing import List


def solution(A: List[int]) -> int:
    if not any(A):
        return 0

    tickets = {
        1: 2,
        7: 7,
        30: 25,
    }

    import sys
    min_cost = sys.maxsize
    size = len(A)

    for length, price in tickets.items():
        current_cost = price
        idx = 0

        last_day = A[idx] + length

        while idx < size and A[idx] < last_day:
            idx += 1

        if current_cost > min_cost:
            continue

        current_cost += solution(A[idx:])
        if current_cost < min_cost:
            min_cost = current_cost

    return min_cost


if __name__ == '__main__':
    cases = {
        11: [1, 4, 6, 7, 28, 30],
        9: [1, 7, 8, 9, 10],
    }

    for expect, parameters in cases.items():
        status = (expect == solution(parameters))
        print("case pass status: %s, detail: %s == solution(%s)" %
              (status, expect, parameters))

答案 2 :(得分:0)

public class Main03v3
{
  public static void main(String[] args)
  {
    int[] A = {1,7,8,9,10,15,16,17,18,21,25};

    System.out.println("Traveling days:\r\n "+Arrays.toString(A));
    int cost = solution(A);
    System.out.println("\r\nMinimum cost is " + cost);
    System.out.println("\r\n" + new String(new char[40]).replace("\0", "-"));
  }

  public static int solution(int[] A) 
  {
    if (A == null) return -1;

    int sevenDays = 7;
    int dayCost = 2, weekCost = 7, monthCost = 25;
    int ratio_WeekAndDays = weekCost / dayCost; 

    int len = A.length;
    if (len == 0) return -1;

    if (len <= 3) return len * dayCost;

    int cost[] = new int[len];

    int i = 0;
    while (i < len)
    {
      int startIdx = i, endIdx = i + 1; 
      while (endIdx < len && A[endIdx]-A[startIdx] < sevenDays) 
        endIdx++;

      if (endIdx-startIdx > ratio_WeekAndDays)
      {
        if (endIdx >= startIdx + sevenDays) 
          endIdx = startIdx + sevenDays;  

        int j = startIdx;
        cost[j] = ((j == 0) ? 0 : cost[j-1]) + weekCost;

        while (++j < endIdx) {
          cost[j] = cost[j-1];
        }  
        i = j;
      }
      else
      {
        cost[i] = ((i == 0) ? 0 : cost[i-1]) + dayCost;
        i++;
      }  
    }
    int finalCost = Math.min(cost[len-1], monthCost);

    return finalCost;  
  }
}

答案 3 :(得分:0)

使用JavaScript查找最低机票价格 情况1:如果输入为[1,7,8,9,10],则所需的输出为9 情况2:如果输入为[1,7,8,9,10,15],则所需的输出为11

function calMinCosts(arr){
    if(!arr || arr.length===0)
        return 0;

    var len = arr.length; 
    var costsOfDateArr = Array.apply(null,{length:arr[len-1]+1}).map(()=>0);

    var price1=2,price2=7,price3=25;
    var days=7;

    var index=0,n=costsOfDateArr.length;
    for(var i=1;i<n;i++){
        if(i===arr[index]){
            if(i>=days+1){
                costsOfDateArr[i] = Math.min(costsOfDateArr[i-days-1]+price2, costsOfDateArr[i-1]+price1);
            }else{
                costsOfDateArr[i] = Math.min(costsOfDateArr[0]+price2, costsOfDateArr[i-1]+price1);
            }
            index+=1;
        }else{
            costsOfDateArr[i] = costsOfDateArr[i-1];
        }
    }

    return Math.min(price3,costsOfDateArr[n-1]);
}

console.log(calMinCosts([1,7,8,9,10]))
console.log(calMinCosts([1,7,8,9,10,15]))

答案 4 :(得分:-1)

这是包含打印输出的C ++解决方案

#include <vector>
#include <iostream>
#include <cmath>
#include <algorithm>
int compute(std::vector<int> &A)
{
int sum[A.size()][A.size()+1];
for (int i = 0; i < A.size(); i++)
{
    for(int j =0; j < A.size(); j++)
    {
        sum[i][j]=2;
    }
}

for (int k = 0; k < A.size();k++)
{
    sum[k][A.size()]=0;
}

for (int i = 0; i < A.size(); i++)
{
    for(int j = 0; j < A.size(); j++)
    {
        if (i!=j)
        {
            if (sum[i][i] != 7)
            {
                int temp = abs(A[j]-A[i]);
                if (temp<7 && abs(j-i)>=3)
                {   
                    sum[i][i]=7;
                    sum[i][j]=7;
                    if (i>j)
                    {
                        for(int k = j;k < i;k++)
                            sum[i][k]=7;
                    }
                    else
                    {
                        for(int k = i;k < j;k++)
                            sum[i][k]=7;
                    } 
                }
            }
        }
    }
}

for (int i = 0; i < A.size(); ++i)
{
    for(int j = 0; j < A.size(); ++j)
    {
        if (sum[i][j]==7)
        {
            sum[i][A.size()]+=1;
        }
    }
}

for (int i = 0; i < A.size(); ++i)
{
    for (int j = 0; j < A.size()+1; ++j)
        std::cout<<sum[i][j]<<" ";
    std::cout<<std::endl;
}


int result = 0;
int row = A.size()-1;
int column = A.size()-1;
while(1)
{
    int value = sum[row][A.size()];
    if (value == 0)
        value=1;
    int temp = sum[row][column];
    result += temp;
    row = row-value;
    column = column-value;
    while (sum[row][column+1]==7 && row>=0)
    {
        row-=1;
        column-=1;
        result+=2;
    }
    if (row < 0)
        break;
}

return result;
}

int solution(std::vector<int> &A) {
if (A.size() > 24)
    return 25;
if (A.size() <= 3)
    return A.size() * 2;

return std::min(25,compute(A));
}

int main()
{
std::vector<int> AA={1,2,3,4,5,29,30};
std::vector<int> B={1,2,3,4,5};
std::vector<int> A={1,2,3,4,5,9,10,11,12,13,14,17,18,20,21};
std::vector<int> C={1,2,3,12};
std::vector<int> D={1,2,3,4,12,13,14,15,29,30};
std::vector<int> DD={1,2,3,4,5,14,17,18,19,20,23,28,29,30};
std::vector<int> CC={1,2,3,4,5,6,7,9,14,17,18,19,20,23,28,29,30};
std::cout<<solution(AA)<<std::endl;
std::cout<<solution(D)<<std::endl;
std::cout<<solution(B)<<std::endl;
std::cout<<solution(A)<<std::endl;
std::cout<<solution(C)<<std::endl;
std::cout<<solution(DD)<<std::endl;
std::cout<<solution(CC)<<std::endl;
return 0;
}

答案 5 :(得分:-3)

使用与自下而上动态编程相同的方法解决。这是完整的解决方案:

public class PublicTicketCost {
    public static void main(String args[]){
        int[] arr  =  {1,7,8,9,10,15,16,17,18,21,25};
        int[] tDays = {1,7,30};
        int[] tCost = {2,7,25};
        System.out.println(minCost(arr, tDays, tCost));
    }
    public static int minCost(int[] arr, int[] tDays, int[] tCost) {
        int[][] dp = new int[arr.length][tDays.length];

        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < tDays.length; j++) {

                int prevDayIndex = findPrevDayIndex(arr,i,tDays,j);
                int prevCost = prevDayIndex>=0 ? dp[prevDayIndex][tDays.length-1] : 0;
                int currCost = prevCost + tCost[j];
                if(j-1>=0){
                    currCost = Math.min(currCost, dp[i][j-1]);
                }
                dp[i][j] = currCost;
            }
        }
        //print(dp);
        return dp[arr.length-1][tDays.length-1];
    }
    private static void print(int arr[][]){
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[0].length; j++) {
                System.out.print(arr[i][j]+" ");
            }
            System.out.println();
        }
    }
    private static int findPrevDayIndex(int[] arr, int i, int[] days, int j){
        int validAfterDate = arr[i] - days[j];
        if (validAfterDate<1){
            return -1;
        }
        for (int k = i-1; k >= 0; k--) {
            if (arr[k]<=validAfterDate){
                return k;
            }
        }
        return -1;
    }
}

http://ideone.com/sfgxGo