D75【模板】分层图最短路 Dijkstra 算法 P4568 [JLOI2011] 飞行路线

D75【模板】分层图最短路 Dijkstra 算法 P4568 [JLOI2011] 飞行路线_哔哩哔哩_bilibili

 

P4568 [JLOI2011] 飞行路线 - 洛谷

P2939 [USACO09FEB] Revamping Trails G - 洛谷

给一个 𝑛 个点 𝑚 条边的无向图,你可以选择 𝑘 条边免费,求 𝑠 到 𝑡 的最小花费。

 

分层图最短路模型:给定 n 个点 m 条边 k 个决策,求从 s 到 t 的最短距离。

分层图最短路就是在多层平行的图上跑最短路,有两种处理方法。

方法一:直接构建 k+1 层平行的图(本质上是拆点扩边)

  1. 每一层,像普通最短路一样连双向边
  2. 从上一层向下一层连权值为 0 的单向边,如果走了这条边就表示用了一次免费机会
  3. 第 0 层表示使用 0 次免费机会,第 k 层表示使用 k 次免费机会

点的个数扩为 n*(k+1),边的条数扩为 m*(4k+2)。空间复杂度为 O(n*(k+1)+m*(4k+2)),时间复杂度为 O(mk*log(nk))

    image         image

方法二:多开一维记录 k 次决策信息(本质上是扩展状态)

d[i][j] 表示到达 i 用了 j 次免费机会的最小花费

更新时,先更新同层之间(即花费免费机会相同)的最短路,再更新从该层到下一层(即再花费一次免费机会)的最短路

空间复杂度为 O(n*(k+1)+m*2),时间复杂度为 O(mk*log(nk))

注意:没走完 k+1 层,可能已经最小(图中 k=2 时),所以答案要对最后一列的状态点取最小值

相关板子:

D02【模板】最短路 Dijkstra 算法 P4779 单源最短路径 - 董晓 - 博客园

 

// 分层图最短路 分层建图 Dijkstra 算法 O(mk*log(nk))
#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;

const int N=10005*11,M=50005*42;
int h[N],to[M],ne[M],w[M],idx;
void add(int a,int b,int c){
  to[++idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx;
}
int n,m,k,s,t;
int d[N];

void dijkstra(){
  memset(d,0x3f,sizeof d); d[s]=0;
  priority_queue<pii,vector<pii>,greater<pii>> q;
  q.emplace(0,s);
  while(q.size()){
    auto [dd,u]=q.top(); q.pop();
    if(dd!=d[u]) continue; //不是第一次出队就跳过
    for(int i=h[u];i;i=ne[i]){
      int v=to[i];
      if(d[v]>d[u]+w[i]){
        d[v]=d[u]+w[i];
        q.emplace(d[v],v);
      }
    }
  }
}
int main(){
  ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  cin>>n>>m>>k>>s>>t;
  for(int a,b,c;m--;){
    cin>>a>>b>>c;
    add(a,b,c),add(b,a,c); //0层双向边
    for(int i=1;i<=k;i++){
      add(a+i*n,b+i*n,c),add(b+i*n,a+i*n,c); //层内双向边
      add(a+(i-1)*n,b+i*n,0),add(b+(i-1)*n,a+i*n,0); //层间单向边
    }
  }
  dijkstra();
  int ans=2e9;
  for(int i=0;i<=k;i++) ans=min(ans,d[t+i*n]); //没走完k+1层,可能已经最小
  cout<<ans;
}

 

// 分层图最短路 二维数组 Dijkstra 算法 O(mk*log(nk))
#include<bits/stdc++.h>
#define pii pair<int,int>
#define ipi pair<int,pii>
using namespace std;

const int N=10005,M=50005*2;
int h[N],to[M],w[M],ne[M],idx;
void add(int a,int b,int c){
  to[++idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx;
}
int n,m,k,s,t;
int d[N][11]; //d[i][j]表示到达i用了j次免费的最小花费
bool vis[N][11];

void dijkstra(){
  memset(d,0x3f,sizeof d); d[s][0]=0;
  priority_queue<ipi,vector<ipi>,greater<ipi>> q;
  q.push({0,{s,0}});
  while(!q.empty()){
    auto [u,c]=q.top().second; q.pop();
    if(vis[u][c])continue; vis[u][c]=1;
    for(int i=h[u];i;i=ne[i]){
      int v=to[i];
      if(d[v][c]>d[u][c]+w[i]){ //层内走路
        d[v][c]=d[u][c]+w[i];
        q.push({d[v][c],{v,c}});
      }
      if(c<k && d[v][c+1]>d[u][c]){ //层间走路
        d[v][c+1]=d[u][c];
        q.push({d[v][c+1],{v,c+1}});
      }
    }
  }
}
int main(){
  ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  cin>>n>>m>>k>>s>>t;
  for(int i=0,a,b,c;i<m;i++){
    cin>>a>>b>>c;
    add(a,b,c),add(b,a,c);
  }
  
  dijkstra();
  cout<<*min_element(d[t],d[t]+k+1); //没走完k+1层,可能已经最小
}

 

P4822 [BJWC2012] 冻结 - 洛谷

本题的决策内容是花费减半,所以层与层之间的权值不是0,而是这条边原权值的一半。

// 分层图最短路 分层建图 Dijkstra 算法 O(mk*log(nk))
#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;

const int N=55*51,M=1005*202;
int h[N],to[M],ne[M],w[M],idx;
void add(int a,int b,int c){
  to[++idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx;
}
int n,m,k;
int d[N];

void dijkstra(){
  memset(d,0x3f,sizeof d); d[1]=0;
  priority_queue<pii,vector<pii>,greater<pii>> q;
  q.emplace(0,1);
  while(q.size()){
    auto [dd,u]=q.top(); q.pop();
    if(dd!=d[u]) continue;
    for(int i=h[u];i;i=ne[i]){
      int v=to[i];
      if(d[v]>d[u]+w[i]){
        d[v]=d[u]+w[i];
        q.emplace(d[v],v);
      }
    }
  }
}
int main(){
  ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  cin>>n>>m>>k;
  for(int a,b,c;m--;){
    cin>>a>>b>>c;
    add(a,b,c),add(b,a,c); //0层双向边
    for(int i=1;i<=k;i++){
      add(a+i*n,b+i*n,c),add(b+i*n,a+i*n,c); //层内双向边
      add(a+(i-1)*n,b+i*n,c/2),add(b+(i-1)*n,a+i*n,c/2); //层间单向边
    }
  }
  dijkstra();
  int ans=2e9;
  for(int i=0;i<=k;i++) ans=min(ans,d[n+i*n]); //没走完k+1层,可能已经最小
  cout<<ans;
}

 

posted @ 2026-02-18 11:09  董晓  阅读(167)  评论(0)    收藏  举报