【图论】【堆优化的单源路径】LCP 20. 快速公交

简介: 【图论】【堆优化的单源路径】LCP 20. 快速公交

作者推荐

【广度优先搜索】【网格】【割点】【 推荐】1263. 推箱子

LCP 20. 快速公交

小扣打算去秋日市集,由于游客较多,小扣的移动速度受到了人流影响:

小扣从 x 号站点移动至 x + 1 号站点需要花费的时间为 inc;

小扣从 x 号站点移动至 x - 1 号站点需要花费的时间为 dec。

现有 m 辆公交车,编号为 0 到 m-1。小扣也可以通过搭乘编号为 i 的公交车,从 x 号站点移动至 jump[i]*x 号站点,耗时仅为 cost[i]。小扣可以搭乘任意编号的公交车且搭乘公交次数不限。

假定小扣起始站点记作 0,秋日市集站点记作 target,请返回小扣抵达秋日市集最少需要花费多少时间。由于数字较大,最终答案需要对 1000000007 (1e9 + 7) 取模。

注意:小扣可在移动过程中到达编号大于 target 的站点。

示例 1:

输入:target = 31, inc = 5, dec = 3, jump = [6], cost = [10]

输出:33

解释: 小扣步行至 1 号站点,花费时间为 5; 小扣从 1 号站台搭乘 0 号公交至 6 * 1 = 6 站台,花费时间为 10; 小扣从 6 号站台步行至 5 号站台,花费时间为 3; 小扣从 5 号站台搭乘 0 号公交至 6 * 5 = 30 站台,花费时间为 10; 小扣从 30 号站台步行至 31 号站台,花费时间为 5; 最终小扣花费总时间为 33。

示例 2:

输入:target = 612, inc = 4, dec = 5, jump = [3,6,8,11,5,10,4], cost = [4,7,6,3,7,6,4]

输出:26

解释: 小扣步行至 1 号站点,花费时间为 4; 小扣从 1 号站台搭乘 0 号公交至 3 * 1 = 3 站台,花费时间为 4; 小扣从 3 号站台搭乘 3 号公交至 11 * 3 = 33 站台,花费时间为 3; 小扣从 33 号站台步行至 34 站台,花费时间为 4; 小扣从 34 号站台搭乘 0 号公交至 3 * 34 = 102 站台,花费时间为 4; 小扣从 102 号站台搭乘 1 号公交至 6 * 102 = 612 站台,花费时间为 7; 最终小扣花费总时间为 26。

提示:

1 <= target <= 109

1 <= jump.length, cost.length <= 10

2 <= jump[i] <= 106

1 <= inc, dec, cost[i] <= 106

单源路径

分析从0到x的最小花费时间。

情况一:没有坐公交,inc*target。

情况二:做了公交,枚举最后一趟公交i,对于公交只需要考虑三种情况:

1,x就在公交站,就在x下车。

2,在x的前一站下车,x/ jump[i]*jump[i]。

3,在x的后一站下车x/ jump[i]*jump[i]+1。

假定x的前一站是x1,前前一站是x2。

{ x 2 / j u m p [ i ] → i n c x 1 / j u m p [ i ] → c o s t [ i ] x 1 i n c + c o s t [ i ] x 1 下车 x 2 / j u m p [ i ] → c o s t [ i ] x 2 → i n c × j u m p [ i ] x 1 i n c ∗ j u m p [ i ] + c o s t [ i ] x 2 下车 \begin{cases} x2/jump[i]^{inc}_{\rightarrow} x1/jump[i]^{cost[i]}_{\rightarrow} x1 & inc+cost[i] & x1下车\\ x2/jump[i] ^{cost[i]}_{\rightarrow} x2 ^{inc\times jump[i]}_{\rightarrow} x1 & inc*jump[i]+cost[i] & x2下车 \end{cases}{x2/jump[i]incx1/jump[i]cost[i]x1x2/jump[i]cost[i]x2inc×jump[i]x1inc+cost[i]incjump[i]+cost[i]x1下车x2下车

情况三证明类似。

共四种情况:

{ 直接处理 没坐车 出发站点 刚好在站点 2 个下车站点 不在车站 \begin{cases} 直接处理 & 没坐车\\ 出发站点 & 刚好在站点\\ 2个下车站点 & 不在车站\\ \end{cases}直接处理出发站点2个下车站点没坐车刚好在站点不在车站

堆优化的单源路径

由于节点数未知,所以无法用朴素单源路径。初始状态无法知道起点(0)的相连节点,所以求0的最短路径,只能求target的最短路径。

代码

核心代码

typedef pair<long long, int> PAIRLLI;
class Solution {
public:
  int busRapidTransit(int target, int inc, int dec, vector<int>& jump, vector<int>& cost) {
    std::priority_queue<PAIRLLI, vector<PAIRLLI>, greater<PAIRLLI>> minHeap;
    minHeap.emplace(0, target);
    while (minHeap.size())
    {
      const long long llDist = minHeap.top().first;
      const int iCur = minHeap.top().second;
      minHeap.pop();
      if (m_mDis.count(iCur))
      {
        continue;
      }
      m_mDis[iCur] = llDist;
      if (0 == iCur)
      {
        break;
      }   
      minHeap.emplace(llDist+(long long)inc*iCur, 0);
      for (int i = 0 ; i < jump.size(); i++)
      {
        const int x1 = iCur / jump[i] * jump[i];        
        if (x1 != iCur)
        {
          minHeap.emplace(llDist + ((long long)inc) * (iCur - x1) , x1 );
          minHeap.emplace(llDist + ((long long)dec) * (x1 + jump[i] - iCur),x1 +jump[i]);
        }
        else
        {
          minHeap.emplace(llDist + cost[i], x1/jump[i]);
        }
      }
    }
    return m_mDis[0]%1000'000'007;
  }
  unordered_map<int, long long> m_mDis;
};

测试用例

template<class T,class T2>
void Assert(const T& t1, const T2& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{
  int target,  inc,  dec;
  vector<int> jump,  cost;
  {
    Solution sln;
    target = 31, inc = 5, dec = 3, jump = { 6 }, cost = { 10 };
    auto res = sln.busRapidTransit(target, inc, dec, jump, cost);
    Assert(33, res);
  }
  {
    Solution sln;
    target = 612, inc = 4, dec = 5, jump = { 3, 6, 8, 11, 5, 10, 4 }, cost = { 4, 7, 6, 3, 7, 6, 4 };
    auto res = sln.busRapidTransit(target, inc, dec, jump, cost);
    Assert(26, res);
  }
  {
    Solution sln;
    target = 1000000000, inc = 1, dec = 1, jump = { 2 }, cost = { 1000000 };
    auto res = sln.busRapidTransit(target, inc, dec, jump, cost);
    Assert(10953125, res);
  }
  {
    Solution sln;
    target = 954116209, inc = 725988, dec = 636911, jump = { 524425, 158389 }, cost = { 41881, 941330 };
    auto res = sln.busRapidTransit(target, inc, dec, jump, cost);
    Assert(556489627, res);
  }
  
  
}

小优化

可以跳过下车站,直接处理上车站。性能更优化,但排错难道增加。

typedef pair<long long, int> PAIRLLI;
class Solution {
public:
  int busRapidTransit(int target, int inc, int dec, vector<int>& jump, vector<int>& cost) {
    std::priority_queue<PAIRLLI, vector<PAIRLLI>, greater<PAIRLLI>> minHeap;
    minHeap.emplace(0, target);
    while (minHeap.size())
    {
      const long long llDist = minHeap.top().first;
      const int iCur = minHeap.top().second;
      minHeap.pop();
      if (m_mDis.count(iCur))
      {
        continue;
      }
      m_mDis[iCur] = llDist;
      if (0 == iCur)
      {
        break;
      }   
      minHeap.emplace(llDist+(long long)inc*iCur, 0);
      for (int i = 0 ; i < jump.size(); i++)
      {
        const int x1 = iCur / jump[i] * jump[i];  
        minHeap.emplace(llDist + ((long long)inc) * (iCur - x1) + cost[i], x1 / jump[i]);
        if (x1 != iCur)
        {
          minHeap.emplace(llDist + ((long long)dec) * (x1 + jump[i] - iCur) + cost[i], x1 / jump[i] + 1);         
        }     
      }
    }
    return m_mDis[0]%1000'000'007;
  }
  unordered_map<int, long long> m_mDis;
};

2023年3月版

class Solution {

public:

const long long M = 1e9 + 7;

int busRapidTransit(int target, int inc, int dec, vector& jump, vector& cost) {

std::priority_queue<std::pair<long long, int>, std::vector<std::pair<long long, int>>, std::greater<std::pair<long long, int>>> que;

std::unordered_map<int,long long> mPosTime;

auto append = [&](long long llTime, int iPos)

{

bool bHas = mPosTime.count(iPos);

if (bHas && (llTime >= mPosTime[iPos]))

{

return;

}

que.emplace(llTime, iPos);

mPosTime[iPos] = llTime;

};

append(0, target);

long long llRet = inc * (long long)target;

for (; que.size()😉

{

const long long llTime = que.top().first;

const int iPos = que.top().second;

que.pop();

if (llTime > llRet)

{

break;

}

llRet = min(llRet, llTime + inc*(long long)iPos);

for (int i = 0; i < jump.size(); i++)

{

const int iPreCur = iPos / jump[i] * jump[i];

if (iPreCur == iPos)

{

append(llTime + cost[i], iPos / jump[i]);

continue;

};

append(llTime + cost[i] + (long long)inc*(iPos - iPreCur), iPos / jump[i]);

const int iNext = iPreCur + jump[i];

append(llTime + cost[i] + (long long)dec*(iNext - iPos), iPos / jump[i] + 1);

}

}

return llRet % M;

}

};


相关文章
【动态规划】【树形dp】【深度优先搜索】LCP 26. 导航装置
【动态规划】【树形dp】【深度优先搜索】LCP 26. 导航装置
|
7月前
|
算法 测试技术 C++
【动态规划】【前缀和】【C++算法】LCP 57. 打地鼠
【动态规划】【前缀和】【C++算法】LCP 57. 打地鼠
|
2月前
|
人工智能 算法 BI
【算法】 线性DP(C/C++)
【算法】 线性DP(C/C++)
|
7月前
|
算法 测试技术 C#
【图论】 【割点】 【双连通分类】LCP 54. 夺回据点
【图论】 【割点】 【双连通分类】LCP 54. 夺回据点
|
7月前
|
机器学习/深度学习 人工智能 算法
【图论 单源最短路】100276. 最短路径中的边
【图论 单源最短路】100276. 最短路径中的边
|
7月前
|
算法 测试技术 C#
【二分图】【二分图最大匹配】LCP 04. 覆盖
【二分图】【二分图最大匹配】LCP 04. 覆盖
|
7月前
|
算法 测试技术 C#
【深度优化】【广度优先】【状态压缩】864. 获取所有钥匙的最短路径
【深度优化】【广度优先】【状态压缩】864. 获取所有钥匙的最短路径
|
算法
图论算法(最短路、网络流、二分图)
图论算法(最短路、网络流、二分图)
108 0
|
算法 C++
单源最短路的建图
单源最短路的建图
48 0
|
C语言 C++
【二分查找】LCP 18. 早餐组合
【二分查找】LCP 18. 早餐组合
122 0

热门文章

最新文章