分层图
前言
前几天考试了发现这个东西完全不会欸……学了又忘真是讨厌至极QAQ所以又在网上找着看了看写一篇博客备忘。
学习笔记真的很有用!
分层图
这个很容易理解,来源就是在一些最短路的问题上题目又加了比如说主角可以用传送宝石进行折跃之类的问题(针对),即可以选择k条边把这些边的边权变为零。
怎么样解决呢?可以在原图的基础上多复制k个相同的图,并用k条边把图之间的边用0边权边相连。就是:
于是就有人说了,复制一个图不是需要很多的空间吗,直接爆掉了怎么办(╯‵□′)╯︵┻━┻
(是的有这种可能所以图k不能太大)
其实因为都是复制的好像不需要很多空间,因为图基本上都是一样的。所以只需要用二维数组dis记录就行了~(所以可以理解为把二维的图变成了三维,然后有k个0边权的虫洞可以折跃\雾)
只需要走一次就行。这里我用dijkstrua~
模板/例题
①第一道裸题
版权声明:题目我是从https://blog.csdn.net/zhangche0526/article/details/62881066搬的。代码是我的。但是我没有数据所以有可能是错的。
1 #include<iostream> 2 #include<cstdio> 3 #include<utility> 4 #include<functional> 5 #include<queue> 6 #include<algorithm> 7 #include<cstring> 8 using namespace std; 9 const int maxn=1e4,maxm=5e4,maxk=10,INF=~0U>>1; 10 int n,m,k,start,end; 11 struct edge{ 12 int to,next,dis; 13 }e[4*maxm*(maxk+1)+maxk+1]; 14 int ecnt,head[maxn*(maxk+1)+1]; 15 void addedge(int from,int to,int dis) 16 { //这种赋值写法必须加强制类型转换,c11可以不用 17 e[++ecnt]=(edge){to,head[from],dis};head[from]=ecnt; 18 } 19 20 void read(int &x) 21 { 22 bool flag = false; 23 char c; 24 do c=getchar();while(c!='-' and (c < '0' || c > '9')); 25 if(c=='-')flag=true; 26 else x+=c-'0'; 27 c=getchar(); 28 while(c>='0' and c<='9')x*=10,x+=c-'0',c=getchar(); 29 } 30 31 inline void readin() 32 { 33 read(n);read(m);read(k);read(start);read(end); 34 for(int i=1;i<=m;i++) 35 { 36 int a,b,w; 37 read(a);read(b);read(w); 38 //重点来了,开始建边权为零的边 39 for(int j=0;j<=k;j++) 40 { 41 addedge(a+n*j,b+n*j,w);addedge(b+n*j,a+n*j,w); 42 //这两行是建图并复制图,共建造k+1个图 43 if(j!=k)//小心别建到虚无中去~ 44 { 45 addedge(a+n*j,b+n*(j+1),0);addedge(b+n*j,a+n*(j+1),0); 46 //这两行是在图之间建边权为0的边 47 } 48 } 49 } 50 } 51 //分层图的核心已经完成,下面是开心的dijkstrua时间 52 int dis[maxn*(maxk+1)]; 53 bool vis[maxn*(maxk+1)]; 54 typedef pair<int,int> pairr; 55 void dijskra() 56 { 57 priority_queue <pairr,vector<pairr >,greater<pairr > > q; 58 q.push(make_pair(0,start)); 59 memset(dis,0x3f3f3f3f,sizeof(dis)); 60 dis[start]=0; 61 while(!q.empty()) 62 { 63 int u=q.top().second;q.pop(); 64 if(!vis[u]) 65 { 66 vis[u]=1; 67 for(int i=head[u];i;i=e[i].next) 68 { 69 int to=e[i].to; 70 if(dis[u]+e[i].dis<dis[to]) 71 { 72 dis[to]=dis[u]+e[i].dis; 73 q.push(make_pair(dis[to],to)); 74 } 75 } 76 } 77 } 78 } 79 int main() 80 { 81 readin(); 82 dijskra(); 83 cout<<dis[end]; 84 return 0; 85 }
②第二道裸题
(这次有数据了)
P4568 [JLOI2011]飞行路线
https://www.luogu.com.cn/problem/P4568
如果掌握了方法的话这个题和上一道基本上是一模一样的,只需要把板子粘下来就行了。不过我们可以从上一道题发现建的边数太多了啊喂……所以我们这次用一下前面提到的二元数组记录。

1 #include<iostream> 2 #include<cstdio> 3 #include<utility> 4 #include<functional> 5 #include<queue> 6 #include<algorithm> 7 #include<cstring> 8 using namespace std; 9 const int maxn=1e4,maxm=5e4,maxk=10,INF=~0U>>1; 10 int n,m,k,start,end; 11 struct edge{ 12 int to,next,dis; 13 }e[4*maxm*(maxk+1)+maxk+1]; 14 int ecnt,head[maxn*(maxk+1)+1]; 15 void addedge(int from,int to,int dis) 16 { 17 e[++ecnt]=(edge){to,head[from],dis};head[from]=ecnt; 18 } 19 20 void read(int &x) 21 { 22 bool flag = false; 23 char c; 24 do c=getchar();while(c!='-' and (c < '0' || c > '9')); 25 if(c=='-')flag=true; 26 else x=c-'0'; 27 c=getchar(); 28 while(c>='0' and c<='9')x*=10,x+=c-'0',c=getchar(); 29 } 30 int read2() 31 { 32 int x; 33 char c=getchar(); 34 while(c<'0' or c>'9')c=getchar(); 35 x=c-'0';c=getchar(); 36 while(c>='0' and c<='9') 37 x*=10,x+=c-'0',c=getchar(); 38 return x; 39 } 40 41 void readin() 42 { 43 // read(n);read(m);read(k);read(start);read(end); 44 n=read2(),m=read2(),k=read2(),start=read2(),end=read2(); 45 for(int i=0;i<m;i++) 46 { 47 int a,b,w; 48 // read(a);read(b);read(w); 49 a=read2(),b=read2(),w=read2(); 50 //重点来了,开始建边权为零的边 51 for(int j=0;j<=k;j++) 52 { 53 addedge(a+n*j,b+n*j,w);addedge(b+n*j,a+n*j,w); 54 //这两行是建图并复制图,共建造k+1个图 55 if(j!=k)//小心别建到虚无中去~ 56 { 57 addedge(a+n*j,b+n*(j+1),0);addedge(b+n*j,a+n*(j+1),0); 58 //这两行是在图之间建边权为0的边 59 } 60 } 61 } 62 } 63 //分层图的核心已经完成,下面是开心的dijskrua时间 64 int dis[maxn*(maxk+1)]; 65 bool vis[maxn*(maxk+1)]; 66 typedef pair<int,int> pairr; 67 void dijkstra() 68 { 69 priority_queue <pairr,vector<pairr >,greater<pairr > > q; 70 q.push(make_pair(0,start)); 71 memset(dis,0x3f3f3f3f,sizeof(dis)); 72 dis[start]=0; 73 while(!q.empty()) 74 { 75 int u=q.top().second;q.pop(); 76 if(!vis[u]) 77 { 78 vis[u]=1; 79 for(int i=head[u];i;i=e[i].next) 80 { 81 int to=e[i].to; 82 if(dis[u]+e[i].dis<dis[to]) 83 { 84 dis[to]=dis[u]+e[i].dis; 85 q.push(make_pair(dis[to],to)); 86 } 87 } 88 } 89 } 90 } 91 int main() 92 { 93 readin(); 94 dijkstra(); 95 for(int i=1;i<=k;++i) 96 { 97 addedge(end+(i-1)*n,end+i*n,0); 98 } 99 cout<<dis[end+k*n]; 100 return 0; 101 }
1 #include<iostream> 2 #include<cstdio> 3 #include<utility> 4 #include<queue> 5 #include<functional> 6 #include<vector> 7 #include<algorithm> 8 #include<cstring> 9 using namespace std; 10 const int maxn=1e4,maxm=5*1e4,maxk=10; 11 typedef pair<int,int> pairr; 12 struct edge{ 13 int to,nxt,dis; 14 }e[maxm*4]; 15 int n,m,k,start,end,d[maxn][maxk+1]; 16 bool vis[maxn][maxk+1]; 17 inline int read() 18 { 19 int x=0;char c=getchar(); 20 while(!isdigit(c))c=getchar(); 21 while(isdigit(c))x=x*10+c-'0',c=getchar(); 22 return x; 23 } 24 int ecnt,head[maxn]; 25 inline void addedge(int from,int to,int dis) 26 { 27 e[++ecnt]=(edge){to,head[from],dis},head[from]=ecnt; 28 } 29 30 void input() 31 { 32 n=read(),m=read(),k=read(),start=read(),end=read(); 33 for(int i=0;i<m;i++) 34 { 35 int from=read(),to=read(),dis=read(); 36 addedge(from,to ,dis);addedge(to,from,dis); 37 } 38 } 39 40 void dijkstra() 41 { 42 priority_queue <pairr,vector<pairr>,greater<pairr > > q; 43 memset(d,0x3f3f3f3f,sizeof(d)); 44 d[start][0]=0; 45 q.push(make_pair(0,start)); 46 while(!q.empty()) 47 { 48 int v=q.top().second;q.pop(); 49 int kk=v/n; //kk标记当前已用的k次数 50 v%=n; //v变为图中点的位置 51 for(register int i=head[v];i;i=e[i].nxt) 52 { 53 int u=e[i].to,w=e[i].dis; 54 if(d[v][kk]+w<d[u][kk]) 55 { 56 d[u][kk]=d[v][kk]+e[i].dis; 57 q.push(make_pair(d[u][kk],u+(kk)*n)); 58 } 59 if(kk==k)continue;//用完了就算了QAQ 60 if(d[u][kk+1]>d[v][kk])//可以用而且不穿越解非最优就传送 61 { 62 d[u][kk+1]=d[v][kk]; 63 q.push(make_pair(d[u][kk+1],u+(kk+1)*n));//这里编号乘一下以记录层数(因为没有结构体就酱紫了) 64 } 65 } 66 } 67 } 68 int main() 69 { 70 input(); 71 dijkstra(); 72 // cout<<d[end][k];//输出全部用过达到的距离 73 //这个输出被最后一组数据hack了,好像是不用用完k个的。所以我们找个min 74 int ans=0x3f3f3f3f; 75 for(int i=0;i<=k;i++)ans=min(ans,d[end][i]); 76 printf("%d\n",ans); 77 return 0; 78 }
笔者用的是pair记录,好像用结构体能更清楚一些并记录层数,其实没什么区别的。大家随意(干杯)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现