分层图最短路

分层图最短路

在最短路中涉及策略时,将图建为多层,用层间边作为选择。
即,扩展图,对k次决策,建(k+1)层,即每个节点扩展为k+1个。
层间有单向边,如果每次决策的顺序没有影响,就跑一次,否则枚举决策的顺序。

用两个例子说明:

cf 1473e:Minimum Path

题意是无向图最短路,对每条路删去最大权,将最小权*2,求这种情况下的最短路。

  • 两次决策,建三层
  • 两个层间,建权为0和2*z的
  • 对每个原有的边,每层中建双向边,层间从上向下建单向边
  • 0和2*z谁先不定,建两个图
  • vis 中,每个点在所有层中只能被访问一次
  • 如果最短路没有层数长,也要考虑,即对每层的dis[t]求最小值(如果有此情况,最后一层dis[t]是inf)

所以建两层,做两次。

luogu P4568: 飞行路线

同样,建k+1层,层间权为0
可以不需要k次,即对每层的dis求最小值。

#include<bits/stdc++.h>
using namespace std;
const int N=2e4+10,M=7e4+10;
int n,m,k,tot,s,t,a,b,c;
int dis[11*N];
int vis[11*N];
int ver[45*M],edge[45*M],nxt[45*M];
int head[11*N];
void add(int x,int y,int z){
  ver[++tot]=y;
  nxt[tot]=head[x];
  head[x]=tot;
  edge[tot]=z;
}
void dij(int s){
  memset(vis,0,sizeof vis);
  dis[s]=0;priority_queue<pair<int ,int> >q;
  q.push(make_pair(0,s));
  while(q.size()){
      int x=q.top().second;q.pop();
      if(vis[x])continue;
      vis[x]=1;
      for(int i=head[x];i;i=nxt[i]){
          int  y=ver[i];
          int z=edge[i];
          if(dis[y]>dis[x]+z){
              dis[y]=dis[x]+z;
              q.push(
                  make_pair(-dis[y],y)
                  //存进去只是排序 而已
              );
          }
      }
  }
}
int main()
{
  memset(dis,0x3f,sizeof dis);
  tot=0;
  cin>>n>>m>>k;
  cin>>s>>t;
  for(int i=1;i<=m;++i){
    cin>>a>>b>>c;
    add(a,b,c);add(b,a,c);
    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);
    }
  }
  dij(s);
  int mi=dis[t];
  for(int i=1;i<=k;++i){
    mi=min(mi,dis[i*n+t]);
  }
  cout<<mi<<endl;
}

总结

要注意这几个点:

  • k次决策,k+1层
  • 最短路可能比k短
  • 顺序要考虑
  • 注意数组大小,k+1层这样的图,有 ( k + 1 ) × 2 × m + m × 2 × k = ( 4 k + 2 ) m (k+1)\times 2 \times m+m\times 2\times k=(4k+2)m (k+1)×2×m+m×2×k=(4k+2)m个边,节点个数则为 ( k + 1 ) × n (k+1)\times n (k+1)×n
  • 如果涉及多张图,边可以建在一起,但head要分开,dis要分开。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值