分层图
前言
使用邻接表存图,不习惯用的可以理解下思路
正文
什么是分层图?
这是一种问题的算法,基础模型为:
一张图(有向无向、有环无环均可)上,有
比如板子题P2939 [USACO09FEB]Revamping Trails G
分层图怎么解决?
我们可以将一个点建
细细的讲,我们先建一个原图,将原图复制一份,再复制一份……复制
这样的目的是:因为可以免费
最后直接正常跑个最短路即可
实现
这里用板子题的代码来讲解(可以当是题解)
一开始我想用全部二维数组来解决,后来觉得太麻烦就用一维存储
主要讲讲存图建边和输出吧其实因为其它的都和正常最短路一模一样
存图(建边)
其实用vector或者二维edge更舒坦,但是我比较喜欢用一维edge(雾
首先,建边时我们读入的
add_edge(u,v,w);
add_edge(v,u,w);
然后分其实是因为没推出来式子
我们的任务是:每次循环中建立这一层的原图,以及与上一层连接的免费路径(这里
式子自己理解下吧,不多讲了
for(int j=1;j<=k;j++){
add_edge(n*j+u,n*j+v,w);//大边
add_edge(n*j+v,n*j+u,w);//反大边
add_edge(n*(j-1)+u,n*j+v,0);//大边0
add_edge(n*(j-1)+v,n*j+u,0);//反大边0
}
输出
一开始我是循环所有的边找出最小,后来发现不对,改成这样:
int ans=dis[n];
for(int i=1;i<=k;i++){
ans=min(ans,dis[(i+1)*n]);
}cout<<ans<<endl;
意思就是从第
后来一想,无疑选择
cout<<dis[n*k+n]<<endl;
发现同样原理
代码
注意!因为一口气存了很多层,所以edge数组一点要开大一点!
我这里的数组是随便开了一点
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int MAXM=100*50000+9;
struct Edge{
int nxt,to,w;
}edge[MAXM];
struct priority{
int id,w;
bool operator <(const priority &x)const{return w>x.w;}
};
int n,m,k;
priority_queue<priority>q;
int num_edge=0,head[MAXM],dis[MAXM];
bool path[MAXM];
void add_edge(int from,int to,int w){
edge[++num_edge]=(Edge){head[from],to,w};
head[from]=num_edge;
}void Dijkstra(int s){
memset(dis,0x3f,sizeof(dis));
memset(path,0,sizeof(path));
q.push((priority){s,0});
dis[s]=0;
while(!q.empty()){
priority x=q.top();
q.pop();
int id=x.id;
if(path[id])continue;
path[id]=1;
for(int i=head[id];i;i=edge[i].nxt){
int to=edge[i].to;
if(dis[to]>dis[id]+edge[i].w){
dis[to]=dis[id]+edge[i].w;
q.push((priority){to,dis[to]});
}
}
}
}
int main(){
scanf("%d %d %d",&n,&m,&k);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
add_edge(u,v,w);
add_edge(v,u,w);
for(int j=1;j<=k;j++){
add_edge(n*j+u,n*j+v,w);//大边
add_edge(n*j+v,n*j+u,w);//反大边
add_edge(n*(j-1)+u,n*j+v,0);//大边0
add_edge(n*(j-1)+v,n*j+u,0);//反大边0
}
}Dijkstra(1);
// int ans=dis[n];
// for(int i=1;i<=k;i++){
// ans=min(ans,dis[(i+1)*n]);
// }cout<<ans<<endl;
cout<<dis[n*k+n]<<endl;
return 0;
}