繁体   English   中英

查找最低票价

[英]Find minimum cost of tickets

查找在每月的已知日期(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。我发布的解决方案不是供其他人调试的,而是仅供参考。 对于这个问题,我采取了自下而上的动态编程方法。 这种方法正确吗?

令MC( d )表示第1天至d天所有行程的最低费用。 所需的答案是MC(30)。

要计算MC( d ),请注意以下几点:

  • 如果在d天没有旅行,则MC( d )= MC( d − 1)。
    • 作为一种特殊的情况下,MC(d)= 0对于所有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

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

在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])) 

这是包含打印输出的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;
}

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

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

暂无
暂无

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

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