简体   繁体   English

查找最低票价

[英]Find minimum cost of tickets

Find minimum cost of tickets required to buy for traveling on known days of the month (1...30). 查找在每月的已知日期(1 ... 30)旅行所需的最低机票购买价格。 Three types of tickets are available : 1-day ticket valid for 1 days and costs 2 units, 7-days ticket valid for 7 days and costs 7 units, 30-days ticket valid for 30 days and costs 25 units. 提供三种类型的票证:1天票证有效期为1天,费用为2个单位; 7天票证有效期为7天,费用为7个单位; 30天票证有效期为30天,费用为25个单位。

For eg: I want to travel on [1,4,6,7,28,30] days of the month ie 1st, 4th, 6th ... day of the month. 例如:我想在每月的[1,4,6,7,28,30]天旅行,即每月的1号,4号,6号...。 How to buy tickets so that the cost is minimum. 如何购买机票,使费用降至最低。

I tried to use dynamic programming to solve this but the solution is not giving me the correct answer for all cases. 我尝试使用动态编程来解决此问题,但该解决方案并未为所有情况提供正确的答案。 Here is my solution in Java : 这是我在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];
    }
}

The solution doesn't work for {1,7,8,9,10} input, it gives 10 but the correct answer should be 9. Also, for {1,7,8,9,10,15} it give 13 but the correct is 11. I have posted my solution not for other to debug it for me but just for reference. 该解决方案不适用于{1,7,8,9,10}输入,它给出10,但正确答案应该是9。此外,对于{1,7,8,9,10,15}它给出13但是正确的是11。我发布的解决方案不是供其他人调试的,而是仅供参考。 I was taken a bottom-up dynamic programming approach for this problem. 对于这个问题,我采取了自下而上的动态编程方法。 Is this approach correct? 这种方法正确吗?

Let MC( d ) denote the minimum cost that will pay for all trips on days 1 through d . 令MC( d )表示第1天至d天所有行程的最低费用。 The desired answer is then MC(30). 所需的答案是MC(30)。

To calculate MC( d ), observe the following: 要计算MC( d ),请注意以下几点:

  • If there's no trip on day d , then MC( d ) = MC( d − 1). 如果在d天没有旅行,则MC( d )= MC( d − 1)。
    • As a special case, MC( d ) = 0 for all d ≤ 0. 作为一种特殊的情况下,MC(d)= 0对于所有d≤0。
  • Otherwise, the minimum cost involves one of the following: 否则,最低成本涉及以下之一:
    • A 1-day pass on day d . d天的1天通行证。 In this case, MC( d ) = MC( d − 1) + 2. 在这种情况下,MC( d )= MC( d − 1)+ 2。
    • A 7-day pass ending on or after day d . d天或之后结束的7天通行证。 In this case, MC( d ) = min(MC( d − 7), MC( d − 6), …, MC( d − 1)) + 7. 在这种情况下,MC( d )= min(MC( d -7),MC( d -6),…,MC( d -1))+ 7。
      • And since MC is nondecreasing (adding a day never reduces the minimum cost), this can be simplified to MC( d ) = MC( d − 7) + 7. (Hat-tip to Ravi for pointing this out.) 而且由于MC不会减少(一天增加不会降低最低成本),因此可以简化为MC( d )= MC( d -7)+7。(向Ravi提示的提示)。
    • A 30-day pass covering the whole period. 30天的通行证涵盖了整个期间。 In this case, MC( d ) = 25. 在这种情况下,MC( d )= 25。

As you've realized, dynamic programming (bottom-up recursion) is well-suited to this. 如您所知,动态编程(自下而上的递归)非常适合于此。

For ease of coding, I suggest we start by converting the list of days into a lookup table for "is this a trip day?": 为了便于编码,我建议我们首先将天列表转换为“这是旅行日吗?”的查找表:

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

We can then create an array to track the minimum costs, and populate it starting from index 0: 然后,我们可以创建一个数组来跟踪最低成本,并从索引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;
}

And minCostUpThroughDay[30] is the result. minCostUpThroughDay[30]是结果。

You can see the above code in action at: https://ideone.com/1Xx1fd . 您可以在以下位置查看上面的代码: https : //ideone.com/1Xx1fd

One recursive solution in Python3. 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))

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;  
  }
}

Find minimum cost of tickets in JavaScript case 1 : if input is [1,7,8,9,10] then the required output is 9 case 2 : if input is [1,7,8,9,10,15] then the required output is 11 在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])) 

Here is the C++ solution including print outs 这是包含打印输出的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;
}

Solved using the same approach of bottom-up dynamic programming. 使用相同的方法解决了自下而上的动态编程问题。 Here is the full solution : 这是完整的解决方案:

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 http://ideone.com/sfgxGo

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM