【动态规划】【C++】最低票价问题

问题描述:
在一个火车旅行很受欢迎的国度,你提前一年计划了一些火车旅行。在接下来的一年里,你要旅行的日子将以一个名为 d a y s days days 的数组给出。每一项是一个从 1 1 1 365 365 365 的整数。

火车票有三种不同的销售方式:

一张为期一天的通行证售价为 c o s t s [ 0 ] costs[0] costs[0] 美元;
一张为期七天的通行证售价为 c o s t s [ 1 ] costs[1] costs[1] 美元;
一张为期三十天的通行证售价为 c o s t s [ 2 ] costs[2] costs[2] 美元。
通行证允许数天无限制的旅行。 例如,如果我们在第 2 2 2 天获得一张为期 7 7 7 天的通行证,那么我们可以连着旅行 7 7 7 天:第 2 2 2 天、第 3 3 3 天、第 4 4 4 天、第 5 5 5 天、第 6 6 6 天、第 7 7 7 天和第 8 8 8 天。

返回你想要完成在给定的列表 d a y s days days 中列出的每一天的旅行所需要的最低消费。


输入描述:
第一行输入 n u m s nums nums 表示有 n u m s nums nums 组测试
对每组测试用例
第一行输入 m m m
第二行输入具有 m m m 个元素的 d a y s days days 数组, d a y s [ i ] days[i] days[i] 表示你将在 d a y s [ i ] days[i] days[i] 这天旅行
第三行输入具有 3 3 3 个元素的 c o s t s costs costs 数组,具体释义见问题描述

输入示例:
2
6
1 4 6 7 8 20
2 7 15
12
1 2 3 4 5 6 7 8 9 10 30 31
2 7 15


输出描述:
对每组测试数据,输出你想要完成在给定的 d a y s days days 数组中列出的每一天的旅行所需要的最低消费。

输出示例:
11
17


没啥用的提示:
1 < = d a y s . l e n g t h < = 365 1 <= days.length <= 365 1<=days.length<=365
1 < = d a y s [ i ] < = 365 1 <= days[i] <= 365 1<=days[i]<=365
d a y s days days 按顺序严格递增
c o s t s . l e n g t h = = 3 costs.length == 3 costs.length==3
1 < = c o s t s [ i ] < = 1000 1 <= costs[i] <= 1000 1<=costs[i]<=1000


思路:
每一天(或者说每一个旅行日),我们的花费都与今天以前之前所作的决策有关。我可以购买一张一天通行证,也可以在七天之前就买了一张七天通行证或者在三十天之前就买了一张三十天通行证。最后当然取三种情况里面花费最小的那个。转化为状态转移方程就如下所示。

d p [ n ] = m i n ( d p [ m a x ( 0 , n − 1 ) ] + c o s t [ 0 ] , d p [ m a x ( 0 , n − 7 ) ] + c o s t [ 1 ] , d p [ m a x ( 0 , n − 30 ) ] + c o s t [ 2 ] ) dp[n] = min(dp[max(0, n - 1)] + cost[0], dp[max(0, n - 7)] + cost[1], dp[max(0, n - 30)] + cost[2]) dp[n]=min(dp[max(0,n1)]+cost[0],dp[max(0,n7)]+cost[1],dp[max(0,n30)]+cost[2])

其中 n n n 代表第 n n n 天, d p [ n ] dp[n] dp[n] 代表了到第 n n n 天为止所需要的最低消费。

因为一年只有 365 365 365 天,所以 d p dp dp 数组最多只有 365 365 365 个元素(加上无意义的 0 0 0 可能有 366 366 366 个),且每个元素只要一次就能求解,也就是说该算法的复杂度仅为 O ( 365 ) = O ( 1 ) O(365) = O(1) O(365)=O(1)


源代码:

//
//  main.cpp
//  minTicketsCost
//
//  Created by 胡昱 on 2021/12/20.
//

#include <iostream>
using namespace std;

int main(int argc, const char * argv[]) {
    // 共nums组测试
    int nums;
    cin >> nums;
    while((nums--) > 0) {
        // 输入days数组
        int lengthOfDays;
        cin >> lengthOfDays;
        if(lengthOfDays <= 0) {
            // 处理极端情况
            cout << 0 << endl;
            continue;
        }
        int* days = new int[lengthOfDays];
        for(int i = 0; i < lengthOfDays; ++i) {
            cin >> days[i];
        }
        
        // 输入cost数组
        const int lengthOfCost = 3;
        int* cost = new int[lengthOfCost];
        for(int i = 0; i < lengthOfCost; ++i) {
            cin >> cost[i];
        }
        
        // 创建动态规划数组
        // dp[n]代表到第n天为止所需要的最低消费,因此共有366项元素(包括无意义的0)
        const int lengthOfDP = 366;
        int* dp = new int[lengthOfDP];
        // 初始化动态规划数组,即第1个旅行日之前的花费为0
        for(int n = 0; n < days[0]; ++n) {
            dp[n] = 0;
        }
        
        // 开始动态规划
        // 思路为:在旅行日时,我可以购买一张一天通行证、也可以在七天之前买一张七天通行证、也可以在三十天之前买一张三十天通行证,最后当然取三者最小
        // 因此动态规划在最后一个旅行日规划完成后就可以停止了,最终的结果为 dp[days[lengthOfDays - 1]]
        for(int i = 0; i < lengthOfDays; ++i) {
            int n = days[i];
            dp[n] = min(dp[max(0, n - 1)] + cost[0], min(dp[max(0, n - 7)] + cost[1], dp[max(0, n - 30)] + cost[2]));
            
            // 填满与下一个旅行日之间的动态规划数组
            if(i + 1 < lengthOfDays) {
                while((n++) < days[i + 1]) {
                    dp[n] = dp[n - 1];
                }
            }
        }
        
        // 输出结果并释放资源
        cout << dp[days[lengthOfDays - 1]] << endl;
        delete [] days;
        delete [] cost;
        delete [] dp;
    }
}

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值