动态规划实战1-leetcode 983.Minimum Cost For Tickets

本文通过动态规划方法,详细解析了一种旅行者在特定日期出行时如何选择不同期限通行证以达到最小总成本的问题。文章定义了状态dp[i]表示到达第i天的最低成本,并给出了状态转移方程及初始化条件,最终实现了一个有效的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:最简单的动态规划比如爬楼梯、斐波那契数列就不做举例分析了。我们来看一下场景相对复杂的题目。

确定状态 dp[i]表示到第i天所用的最少的cost

状态转移方程(这道题不难写)

dp[i]=dp[i-1] 如果在第i天没有travel

dp[i]= min {dp[i-1]+costs[0],dp[i-7]+costs[1],dp[i-30]+costs[2]} 

初始条件和边界条件

Arrays.fiil(dp,Integer.MAX_VALUE)

dp[0]=0;

我们来看下这个表达式:dp[i-30] 如果i<30说明此时我们应该从第0天就买为期30天的票,而不是dp[负数]。

状态转移方程可以修改表示为如下:

dp[i]=dp[i-1] 如果在第i天没有travel

dp[i]= min {dp[i-1]+costs[0],dp[max{i-7,0}]+costs[1],dp[max{i-30,0}]+costs[2]} 

代码如下:

class Solution {
    public int mincostTickets(int[] days, int[] costs) {
        if(days==null||days.length==0)return 0;
        
        int maxday=days[days.length-1];
        int []dp = new int [maxday+1];
        int []req = new int [maxday+1];
        for(int i=0;i<days.length;i++){
            req[days[i]]=1;
        }
        Arrays.fill(dp,Integer.MAX_VALUE);
        dp[0]=0;
        for(int i=1;i<=maxday;i++){
            if(req[i]==0){
                dp[i]=dp[i-1];
            }else{
                    dp[i]=Math.min(dp[i],dp[i-1]+costs[0]);
                    dp[i]=Math.min(dp[i],dp[Math.max(i-7,0)]+costs[1]);
                    dp[i]=Math.min(dp[i],dp[Math.max(i-30,0)]+costs[2]);
         }
        }
        return dp[maxday];
    }
}

 

#include <bits/stdc++.h> using namespace std; struct Station { double d, price; }; bool cmp(const Station& a, const Station& b) { return a.d < b.d; } int main() { double D1, C, D2, P; int N; cin >> D1 >> C >> D2 >> P >> N; vector<Station> stations; stations.push_back({0.0, P}); // 添加起点 for (int i = 0; i < N; ++i) { double d, p; cin >> d >> p; stations.push_back({d, p}); } stations.push_back({D1, 0.0}); // 添加终点 sort(stations.begin(), stations.end(), cmp); // 检查是否无法到达 bool possible = true; for (int i = 1; i < stations.size(); ++i) { if (stations[i].d - stations[i-1].d > C * D2) { possible = false; break; } } if (!possible) { cout << "No Solution" << endl; return 0; } double current_oil = 0.0; double ans = 0.0; int i = 0; int n = stations.size(); while (i < n - 1) { // 未到达终点 // 寻找下一个价格更低的加油站 int j = -1; for (int k = i + 1; k < n; ++k) { if (stations[k].price < stations[i].price) { j = k; break; } } if (j == -1) j = n - 1; // 没有找到更低的,设为终点 double distance = stations[j].d - stations[i].d; double need = distance / D2; if (current_oil >= need) { current_oil -= need; i = j; } else { double add = need - current_oil; double max_add = C - current_oil; if (add <= max_add) { ans += add * stations[i].price; current_oil += add; } else { ans += max_add * stations[i].price; current_oil = C; } // 行驶到下一个加油站i+1 distance = stations[i+1].d - stations[i].d; double used = distance / D2; current_oil -= used; i++; } } printf("%.2f\n", ans); return 0; }
最新发布
03-15
<think>好的,我现在需要帮助用户解决关于C++代码理解和优化的问题,具体是燃油站最低成本问题。首先,我得确认用户的问题是什么。用户提到了“minimum cost fuel station problem”,可能是指类似LeetCode上的题目,比如“Minimum Cost to Reach Destination with Fuel”或者类似的动态规划问题。根据用户提供的引用[1],原题地址是https://leetcode.com/problems/minimum-cost-for-tickets/,不过那个题目是关于旅行车票的,可能用户提到的燃油站问题类似,但具体题目可能不同。需要先明确问题内容。 接下来,用户需要的是代码解释和优化建议。假设用户已经有一段C++代码,但希望理解其工作原理,并得到优化的建议。在没有看到具体代码的情况下,可能需要给出一般性的建议,或者根据常见解法进行分析。 首先,我需要回忆这类问题的典型解法。例如,燃油站问题可能需要动态规划来解决,考虑每个站点的加油策略,或者在油箱容量限制下的最小成本。例如,每到达一个站点时,可以选择加油或不加油,记录到达该站点时的剩余油量和最小成本。 常见的动态规划状态可能设计为dp[i][f],表示到达第i个站点时剩余油量f的最小成本。然后状态转移需要考虑从之前的站点转移过来,并计算加油后的情况。 对于代码解释,可能需要解释动态规划数组的设计、循环结构、状态转移方程等。优化方面,可能需要考虑空间优化,比如滚动数组,或者剪枝策略,比如去除不可能的状态,或者优先队列优化时间复杂度。 用户的问题可能涉及到如何处理大范围的站点或油箱容量,导致常规DP方法超时或内存不足。这时候可能需要优化状态转移,或者采用贪心策略,比如在每个站点尽可能多加油,或者寻找更优的数据结构来维护状态。 另外,需要检查代码中是否存在冗余计算或低效循环,是否有不必要的条件判断,或者可以合并的状态。例如,如果某些状态明显不会更优,可以提前跳过。 另外,内存使用方面,如果油箱容量较大,使用二维数组可能导致内存问题,可以考虑用哈希表或者只保留必要的前一状态,进行空间压缩。 还需要考虑代码的可读性和结构,比如是否将重复的逻辑封装成函数,变量命名是否清晰,是否有注释帮助理解等。 现在,假设用户提供的代码是使用动态规划的方法,可能存在时间复杂度高的问题,可能需要优化状态转移或采用更高效的算法结构。 例如,对于每个站点,可能需要遍历所有可能的剩余油量,这会导致时间复杂度为O(N*C),其中N是站点数,C是油箱容量。如果C很大,这可能不够高效。此时,可能需要寻找贪心策略,或者将问题转化为图的最短路径问题,使用Dijkstra算法,优先队列来处理,这样时间复杂度可以降低到O(M log M),其中M是边的数量。 因此,优化建议可能包括改用贪心或Dijkstra算法,使用优先队列来选择当前成本最低的状态进行扩展,避免不必要的状态遍历。 此外,检查代码中的边界条件,比如起始点和终点的处理,油箱初始油量的设置是否正确,是否处理了无法到达终点的情况等。 总结来说,针对用户的请求,我需要分两部分回答:代码解释和优化建议。代码解释部分需要基于常见的动态规划方法,解释状态定义、转移方程等。优化建议则需要考虑时间复杂度和空间优化,可能涉及算法替换或数据结构调整,同时检查代码中的实现细节是否有改进空间。</think>[用户可见层] ### C++代码解释与优化建议(燃油站最低成本问题) #### 一、代码结构解析 假设代码采用动态规划方法解决燃油站问题,典型实现可能包含以下要素: 1. **状态定义**:使用二维数组`dp[i][f]`表示到达第$i$个站点时剩余油量$f$的最小成本 2. **初始化**:起点油量初始化为满油状态,例如`dp[0][C] = 0`($C$为油箱容量)[^1] 3. **状态转移**: - 从站点$i-1$到$i$消耗油量$d$,更新剩余油量:`dp[i][f-d] = min(dp[i][f-d], dp[i-1][f])` - 在站点$i$加油$k$升:`dp[i][f+k] = min(dp[i][f+k], dp[i][f] + cost[i]*k)` #### 二、优化建议 1. **时间复杂度优化** - **Dijkstra算法**:将问题转化为图论问题,节点表示(站点, 剩余油量),用优先队列选择当前成本最低的状态扩展[^3] ```cpp priority_queue<pair<int, pair<int, int>>, vector<...>, greater<...>> pq; // (总成本, 站点, 剩余油量) ``` - **剪枝策略**:若当前状态的油量更高且成本更高,则跳过该状态 2. **空间复杂度优化** - **滚动数组**:仅保留前一个站点的状态,将二维数组压缩为两个一维数组 ```cpp vector<int> prev_dp(C+1, INF); vector<int> curr_dp(C+1, INF); ``` 3. **实现细节优化** - **预处理距离**:提前计算相邻站点间的油耗,避免重复计算 - **油量离散化**:若油量取值范围大,按固定步长(如每次加1升)离散化处理 #### 三、代码示例(优化后片段) ```cpp struct State { int cost, station, fuel; bool operator>(const State& other) const { return cost > other.cost; } }; int minCost(vector<int>& stations, int C) { priority_queue<State, vector<State>, greater<>> pq; pq.push({0, 0, C}); // 初始状态:成本0,第0站,满油 while (!pq.empty()) { auto [cur_cost, cur_station, cur_fuel] = pq.top(); pq.pop(); if (cur_station == stations.size()) return cur_cost; // 加油扩展 for (int add = 1; cur_fuel + add <= C; ++add) { int new_cost = cur_cost + stations[cur_station] * add; pq.push({new_cost, cur_station, cur_fuel + add})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值