当前位置: 首页 > news >正文

[算法日常] 分层图最短路

[算法日常] 分层图最短路

定义

对于一个可以跑最短路的图 G G G,有 k k k 次可以 改变权值 的机会的问题,我们叫它分层图最短路。

前置知识

  • 最短路(建议使用 dijkstra)
  • dp

解法

解法1:二维dp

首先根据 dijkstra 算法中的松弛操作数组 dis[i] 入手,原意是表示点 i i i 到起点 s s s 的最短路。

那么可以多设一维,dis[i][j] 表示节点 i i i 用了 j j j 次机会时距离 s s s 的最短路。

那么在跑最短路的过程中,在松弛操作里,就可以把状态转移方程推一下:
d i s i , j = m i n ( m i n ( d i s f r o m , j + w ) , m i n ( d i s f r o m , j − 1 ) ) dis_{i,j}=min(min(dis_{from,j}+w),min(dis_{from,j-1})) disi,j=min(min(disfrom,j+w),min(disfrom,j1))
上面意思是松弛操作看看是不用机会好还是用了机会好。

解法2:多建点边

这种方法我认为是最适合萌新(比如我)学的解法。因为它十分好理解。

设我们改变的权值为 w w w

原图可认为是第一层的原图,而此方法是再新建了 k k k 层,每层对应的节点用 w w w 连接。

例子:

假设我们有这么一张图:

其中 k = 1 k=1 k=1

那么我们建的图就是这样的:

十分抽象

注意到,我们真正的节点仅有 1 ∼ 5 1\sim 5 15,而我们却建了 1 ∼ k × n 1\sim k\times n 1k×n,共 k k k​ 层,中间用可修改的权值连接。

且对应的 i × n + u i\times n+u i×n+u 连接的肯定是对应的 ( i + 1 ) × n + v (i+1)\times n+v (i+1)×n+v ( i − 1 ) × n + v (i-1)\times n+v (i1)×n+v

这么做也就是分层图的名字来源。

那么很显然了,我们就从 s s s 号节点做最短路,跑到我们需要的节点 t t t,并且再取个 m i n ( d i s 1 × n + t ∼ d i s k ∗ n + t ) min(dis_{1\times n+t}\sim dis_{k*n+t}) min(dis1×n+tdiskn+t)。因为有可能 k k k 次机会没有用完。

或者不用取最小值,可以在每个 i × n + t i\times n+t i×n+t 连个 w w w 的边。最后直接求 d i s k × n + t dis_{k\times n+t} disk×n+t​。

易错点

注意边数!!!特别是打链式前向星的同学们(比如我)很经常栽在没建够图上,请算清楚,一共有 4 × ( k + 1 ) × n 4\times (k+1)\times n 4×(k+1)×n 条边!

例题:

[JLOI2011] 飞行路线

显然分层图,且 w w w 0 0 0

代码:

#include<bits/stdc++.h>
using namespace std;
#define ljl long long
#define PII pair<ljl,ljl>
#define mk make_pair
const ljl K=15,M=2e6+5,N=(1e4+5)*K,inf=1e18;
ljl n,m,k,head[N],cnt_e,u,v,w,s,t,dis[N],ans;
bool vis[N];
priority_queue <PII ,vector < PII > , greater< PII > > heap;
struct E{ljl to,w,pre;
}e[M<<1];
inline void add(ljl from,ljl to,ljl w)
{e[++cnt_e].to=to;e[cnt_e].w=w;e[cnt_e].pre=head[from];head[from]=cnt_e;return;
}
inline void init()
{memset(dis,0x3f3f3f3f,sizeof(dis));memset(vis,0,sizeof(vis));while(!heap.empty())heap.pop();return;
}
inline void dijk()
{init();dis[s]=0;heap.push(mk(0,s));while(!heap.empty()){ljl u=heap.top().second;heap.pop();if(vis[u]) continue;vis[u]=true;for(ljl i=head[u];i;i=e[i].pre){ljl v=e[i].to;if(dis[v]>dis[u]+e[i].w){dis[v]=dis[u]+e[i].w;heap.push(mk(dis[v],v));}}}return;
}
int main(){scanf("%lld%lld%lld",&n,&m,&k);scanf("%lld%lld",&s,&t);for(ljl i=1;i<=m;i++){scanf("%lld%lld%lld",&u,&v,&w);add(u,v,w);add(v,u,w);//重点建图!!!!!!for(ljl j=1;j<=k;j++)//往下建k层{add(u+(j-1)*n,v+j*n,0);add(v+(j-1)*n,u+j*n,0);add(u+j*n,v+j*n,w);add(v+j*n,u+j*n,w);}}for(ljl i=1;i<=k;i++)//上述,最后直接取最小值即可,不用考虑是否用完k次机会add(t+(i-1)*n,t+i*n,0);dijk();printf("%lld\n",dis[t+k*n]);return 0;
}
[BJWC2012] 冻结

解题思路显然,不过唯一不同就是修改的边权不是 0 0 0,而是 w 2 \frac{w}{2} 2w

#include<bits/stdc++.h>
using namespace std;
#define ljl long long
#define PII pair<ljl,ljl>
#define mk make_pair
const ljl K=25,M=5e6+5,N=(1e4+5)*K,inf=1e18;
ljl n,m,k,head[N],cnt_e,u,v,w,s,t,dis[N],ans;
bool vis[N];
priority_queue <PII ,vector < PII > , greater< PII > > heap;
struct E{ljl to,w,pre;
}e[M<<1];
inline void add(ljl from,ljl to,ljl w)
{e[++cnt_e].to=to;e[cnt_e].w=w;e[cnt_e].pre=head[from];head[from]=cnt_e;return;
}
inline void init()
{memset(dis,0x3f3f3f3f,sizeof(dis));memset(vis,0,sizeof(vis));while(!heap.empty())heap.pop();return;
}
inline void dijk()
{init();dis[s]=0;heap.push(mk(0,s));while(!heap.empty()){ljl u=heap.top().second;heap.pop();if(vis[u]) continue;vis[u]=true;for(ljl i=head[u];i;i=e[i].pre){ljl v=e[i].to;if(dis[v]>dis[u]+e[i].w){dis[v]=dis[u]+e[i].w;heap.push(mk(dis[v],v));}}}return;
}
int main(){scanf("%lld%lld%lld",&n,&m,&k);s=1,t=n;for(ljl i=1;i<=m;i++){scanf("%lld%lld%lld",&u,&v,&w);add(u,v,w);add(v,u,w);for(ljl j=1;j<=k;j++){add(u+(j-1)*n,v+j*n,w/2);add(v+(j-1)*n,u+j*n,w/2);add(u+j*n,v+j*n,w);add(v+j*n,u+j*n,w);}}for(ljl i=1;i<=k;i++)add(t+(i-1)*n,t+i*n,0);dijk();printf("%lld\n",dis[t+k*n]);return 0;
}

是的,我压根就没有重新打代码,就是改了一些细节。

总结

分层图最短路实现不难,难在它的思路以及变通。之所以从 提高+/省选- -> 普及+/提高 可能就是因为 CCF 今年重视了思路应用,而不是代码实现吧。。

预祝大家 CSP-J/S 2024 RP++!!!

相关文章:

  • mock方法内容的匿名方法
  • 关于HTML 案例_个人简历展示02
  • 动手学深度学习(李沐)PyTorch 第 3 章 线性神经网络
  • Jmeter的使用方法
  • python中提示‘pyinstaller‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。
  • vue3:路由守卫(全局守卫、路由独享守卫、组件内守卫)
  • 15、网络安全合规由来与要素
  • 应用性能管理工具-SkyWalking
  • 目前最好用的爬虫软件是那个?
  • C++游戏
  • 追梦无Bug的软件世界
  • Web3.0 应用项目
  • Conda虚拟环境配置常见问题记录
  • 微服务sentinel解析部署使用全流程
  • 《RabbitMQ篇》Centos7安装RabbitMQ
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • “大数据应用场景”之隔壁老王(连载四)
  • 《深入 React 技术栈》
  • Asm.js的简单介绍
  • Create React App 使用
  • E-HPC支持多队列管理和自动伸缩
  • Flex布局到底解决了什么问题
  • GraphQL学习过程应该是这样的
  • java8-模拟hadoop
  • Java深入 - 深入理解Java集合
  • Js基础——数据类型之Null和Undefined
  • Rancher如何对接Ceph-RBD块存储
  • redis学习笔记(三):列表、集合、有序集合
  • socket.io+express实现聊天室的思考(三)
  • vuex 学习笔记 01
  • 通过git安装npm私有模块
  • 微信公众号开发小记——5.python微信红包
  • 用Python写一份独特的元宵节祝福
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 自动记录MySQL慢查询快照脚本
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • Prometheus VS InfluxDB
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • #NOIP 2014#day.2 T1 无限网络发射器选址
  • (3)(3.5) 遥测无线电区域条例
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (vue)页面文件上传获取:action地址
  • (几何:六边形面积)编写程序,提示用户输入六边形的边长,然后显示它的面积。
  • (力扣)循环队列的实现与详解(C语言)
  • (论文阅读30/100)Convolutional Pose Machines
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (七)理解angular中的module和injector,即依赖注入
  • (一)、python程序--模拟电脑鼠走迷宫
  • (一)十分简易快速 自己训练样本 opencv级联haar分类器 车牌识别
  • (一一四)第九章编程练习
  • (转)memcache、redis缓存
  • .net core + vue 搭建前后端分离的框架