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