Leetcode 2473. 购买苹果的最低成本【最短路Dijkstra】

Leetcode 2473. 购买苹果的最低成本

给你一个正整数  n,表示从 1 到 n 的 n 个城市。还给你一个 二维 数组 roads,其中 roads[i] = [ai, bi, costi] 表示在城市 ai 和 bi 之间有一条双向道路,其旅行成本等于 costi。

 

你可以在 任何 城市买到苹果,但是有些城市买苹果的费用不同。给定数组 appleCost ,其中 appleCost[i] 是从城市 i 购买一个苹果的成本。

你从某个城市开始,穿越各种道路,最终从 任何一个 城市买 一个 苹果。在你买了那个苹果之后,你必须回到你 开始的 城市,但现在所有道路的成本将 乘以 一个给定的因子 k。

给定整数 k,返回一个大小为 n 的从 1 开始的数组 answer,其中 answer[i] 是从城市 i 开始购买一个苹果的 最小 总成本。

 

示例 1:


输入: n = 4, roads = [[1,2,4],[2,3,2],[2,4,5],[3,4,1],[1,3,4]], appleCost = [56,42,102,301], k = 2
输出: [54,42,48,51]
解释: 每个起始城市的最低费用如下:
- 从城市 1 开始:你走路径 1 -> 2,在城市 2 买一个苹果,最后走路径 2 -> 1。总成本是 4 + 42 + 4 * 2 = 54。
- 从城市 2 开始:你直接在城市 2 买一个苹果。总费用是 42。
- 从城市 3 开始:你走路径 3 -> 2,在城市 2 买一个苹果,最后走路径 2 -> 3。总成本是 2 + 42 + 2 * 2 = 48。
- 从城市 4 开始:你走路径 4 -> 3 -> 2,然后你在城市 2 购买,最后走路径 2 -> 3 -> 4。总成本是 1 + 2 + 42 + 1 * 2 + 2 * 2 = 51。
示例 2:


输入: n = 3, roads = [[1,2,5],[2,3,1],[3,1,2]], appleCost = [2,3,1], k = 3
输出: [2,3,1]
解释: 在起始城市买苹果总是最优的。
 

提示:

2 <= n <= 1000
1 <= roads.length <= 1000
1 <= ai, bi <= n
ai != bi
1 <= costi <= 105
appleCost.length == n
1 <= appleCost[i] <= 105
1 <= k <= 100
没有重复的边。

解题思路

从A点出发,到B点买苹果,然后回到A点,要求找到最小花费。先不考虑买完苹果之后的成本要乘上因子K这一限制,然后贪心地考虑一下,A->B 和 B->A 来回都选择最短路才是最优的,而最短路的花费是唯一确定的,所以来回的花费是一样的,那么总花费是2倍的最短路。此时再考虑因子K,买完苹果之后,因子K对所有路径生效,所以虽然最短路变贵了,但其它的路只会更贵,最短路还是“那条路径”,因子K并不影响“该怎么走”,只是路费要整个乘上k。假设A->B 最短路的花费是 cost,那么 B->A 按照最短路原路返回,花费为 cost * k ,那么来回的总花费为 cost * (k+1).

以A为源点,求解单源最短路,然后枚举B点(也就是买苹果的点),min(dist(A,B)+appleCost(B) ) 即为anser[A] ,但题目要求所有的anser,那么还需要枚举A点作为源点,依次求解anser[A]。时间复杂度为 (n^2 log n) .

考虑优化一下,建立一个虚拟源点 X,X与任意城市之间都有一条路,花费为在目标城市买苹果的成本。那么题意就可以转化一下,变成求从源点X开始,到任意城市的最短路(乘上(k+1))。此时只需要用Dijkstra求解一次即可,时间复杂度为 N*logN 。

class Solution {
public:
    vector<long long> minCost(int n, vector<vector<int>>& roads, vector<int>& appleCost, int k) {
        vector<vector<pair<int,int>>>G(n+1); // 邻接矩阵存图
        for(auto &road:roads){
            int &a=road[0],&b=road[1],cost=road[2]*(k+1); // 直接将边权扩大。或者,如果这里不扩大,那么在最终答案那里扩大倍数
            G[a].emplace_back(b,cost);
            G[b].emplace_back(a,cost);
        }
        vector<long long>dis(n+1);
        vector<int>vis(n+1);
        auto cmp=[](pair<int,int>&a,pair<int,int>&b){
            return a.second>b.second;
        };
        priority_queue<pair<int,int>,vector<pair<int,int>>,decltype(cmp)>q(cmp);
        // 不必真的把虚拟源点X在图上建立出来,而是直接把所有城市放进初始队列里。
        for(int i=1;i<=n;i++){
            dis[i]=appleCost[i-1];// 初始成本即为买苹果的成本
            q.emplace(i,dis[i]);
        }
        while(!q.empty()){
            int u=q.top().first;
            q.pop();
            if(vis[u])continue;
            vis[u]=true;
            for(auto &[v,cost]:G[u]){
                if(dis[v]>dis[u]+cost){ // Dijkstra 松弛操作
                    dis[v]=dis[u]+cost;
                    q.emplace(v,dis[v]);
                }
            }
        }
        dis.erase(dis.begin());
        return dis;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值