【图论】分层图

一、分层图的核心思想

分层图是一种将图的不同状态拆分为多个“层”的建模方法,每层对应一种特定状态。通过这种方式,可以将复杂的状态转移问题转化为多层图中的最短路径问题。

核心特点

  • 层内边:表示普通操作(如正常行走)。
  • 层间边:表示状态转移(如使用一次特殊能力、改变状态等)。
  • 状态压缩:通常通过 j * n + u 的方式编号节点(j 表示层数,u 是原图节点)。

二、分层图的构建方法

  1. 物理构图

    • 定义:直接将图复制为 k 层,每层节点数为 n
    • 层内边:与原图相同,边权不变。
    • 层间边:按状态转移规则添加(如使用一次特殊能力)。
    • 适用场景k 较小(如 k ≤ 10)。
    • 缺点:空间消耗大(总节点数为 k * n)。
  2. DP 构图

    • 定义:通过动态规划模拟状态转移,无需显式构建多层图。
    • 状态表示:使用二维数组 dis[u][j] 表示在节点 u、状态 j 下的最短距离。
    • 适用场景:状态维度较大的问题(如时间、钥匙状态等)。
    • 优点:节省空间,适合高维状态。

三、分层图的典型应用场景

  1. 有限次决策

    • 例题:允许最多使用 k 次特殊能力(如免费边、升级等)。
    • 建模:构建 k+1 层图,层间边表示使用能力。
  2. 状态依赖

    • 例题:钥匙状态、时间余数、奇偶性等。
    • 建模:根据状态分层(如钥匙状态用二进制编码)。
  3. 多维状态转移

    • 例题:同时考虑时间、资源、权限等状态。
    • 建模:每个维度对应一层,组合成多维分层图。

四、分层图的详细例题与实现

例题 1:JLOI2011 飞行路线(允许 k 次免费边)

问题描述
给定一个无向图,最多可以选择 k 条边免费,求从起点到终点的最短路径。

分层图建模

  • 构建 k+1 层图(0~k 层)。
  • 层内边:原图边权不变。
  • 层间边:从第 j 层的 u 到第 j+1 层的 v,边权为 0(表示使用一次免费机会)。

C++ 实现

#include <bits/stdc++.h>
using namespace std;const int N = 1e5 + 10, M = 1e6 + 10;
int h[N * 10], e[M * 2], ne[M * 2], w[M * 2], idx;
bool st[N * 10];
int dis[N * 10];void add(int a, int b, int c) {e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}void dijkstra(int n, int k) {priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;memset(dis, 0x3f, sizeof dis);dis[1] = 0;pq.push({0, 1});while (pq.size()) {auto [d, u] = pq.top();pq.pop();if (st[u]) continue;st[u] = true;for (int i = h[u]; ~i; i = ne[i]) {int v = e[i], cost = w[i];if (dis[v] > d + cost) {dis[v] = d + cost;pq.push({dis[v], v});}}}
}int main() {int n, m, k;cin >> n >> m >> k;memset(h, -1, sizeof h);for (int i = 0; i < m; i++) {int a, b, c;cin >> a >> b >> c;for (int j = 0; j <= k; j++) {int u = a + j * n, v = b + j * n;add(u, v, c), add(v, u, c); // 层内边if (j < k) {int u2 = a + (j + 1) * n, v2 = b + (j + 1) * n;add(u, v2, 0), add(v, u2, 0); // 层间免费边add(v, v2, 0), add(u, u2, 0);}}}dijkstra(n, k);int res = 0x3f3f3f3f;for (int j = 0; j <= k; j++) {res = min(res, dis[n + j * n]);}cout << res << endl;return 0;
}

关键点

  • 节点编号u + j * n 表示第 j 层的节点 u
  • 层间边:允许最多 k 次免费升级,边权为 0。

例题 2:CSP-J 2023 旅游巴士(时间余数分层)

问题描述
给定发车间隔 k,求从起点到终点的最短时间(允许等待多轮车次)。

分层图建模

  • 按时间余数 t % k 分层,共 k 层。
  • 状态 (u, t % k):表示当前在节点 u,时间余数为 t % k
  • 边权动态调整:根据当前时间和发车间隔 k 计算等待时间。

C++ 实现

#include <bits/stdc++.h>
using namespace std;const int N = 1e5 + 10, K = 1e3 + 10;
int h[N], e[N * 2], ne[N * 2], w[N * 2], idx;
int dis[N][K]; // dis[u][j]: 节点u,余数j的最短时间void add(int a, int b, int c) {e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}void dijkstra(int n, int k) {priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<>> pq;memset(dis, 0x3f, sizeof dis);dis[1][0] = 0;pq.push({0, 1, 0});while (!pq.empty()) {auto [d, u, t_mod] = pq.top();pq.pop();if (d > dis[u][t_mod]) continue;for (int i = h[u]; ~i; i = ne[i]) {int v = e[i], cost = w[i];int current_time = d;if (current_time < cost) {// 需要等待int delta = ((cost - current_time + k - 1) / k) * k;int new_time = current_time + delta;int new_mod = new_time % k;if (dis[v][new_mod] > new_time) {dis[v][new_mod] = new_time;pq.push({new_time, v, new_mod});}} else {// 直接走int new_time = current_time + 1;int new_mod = new_time % k;if (dis[v][new_mod] > new_time) {dis[v][new_mod] = new_time;pq.push({new_time, v, new_mod});}}}}
}int main() {int n, m, k;cin >> n >> m >> k;memset(h, -1, sizeof h);for (int i = 0; i < m; i++) {int a, b, c;cin >> a >> b >> c;add(a, b, c), add(b, a, c);}dijkstra(n, k);int res = 0x3f3f3f3f;for (int j = 0; j < k; j++) {res = min(res, dis[n][j]);}cout << res << endl;return 0;
}

关键点

  • 状态压缩dis[u][j] 表示节点 u 在余数 j 下的最短时间。
  • 动态调整时间:根据当前时间和发车间隔 k 计算等待时间。

五、分层图的扩展应用

例题 3:孤岛营救问题(钥匙状态分层)

问题描述
网格图中,门需要钥匙开启,钥匙分布在格子中,求从起点到终点的最短路径。

分层图建模

  • 钥匙状态:用二进制表示钥匙状态(如 101 表示有钥匙 1 和 3)。
  • 层内边:普通移动(墙或门未解锁时无法通过)。
  • 层间边:拾取钥匙后状态转移。

C++ 实现(逻辑构图):

#include <bits/stdc++.h>
using namespace std;const int N = 11, M = 100;
int h[M], e[M], ne[M], w[M], idx;
int dis[M][1 << 10]; // dis[u][state]: 节点u,钥匙状态state的最短距离void add(int a, int b, int c) {e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}void bfs(int n, int m, int p) {queue<pair<int, int>> q;memset(dis, 0x3f, sizeof dis);dis[0][0] = 0; // 起点(0,0),无钥匙q.push({0, 0});while (!q.empty()) {auto [u, state] = q.front();q.pop();for (int i = h[u]; ~i; i = ne[i]) {int v = e[i], cost = w[i];int new_state = state;// 检查是否需要钥匙if (需要钥匙) {if (state 有钥匙) {new_state = state | 钥匙状态;} else {continue; // 无法通过}}if (dis[v][new_state] > dis[u][state] + cost) {dis[v][new_state] = dis[u][state] + cost;q.push({v, new_state});}}}
}

六、分层图的优化技巧

  1. 空间优化

    • 物理构图:总节点数为 k * n,边数为 k * m + ...
    • DP 构图:状态数为 n * 2^pp 为钥匙种类数)。
  2. 时间优化

    • 使用 01BFS(边权为 0 或 1 时)。
    • 使用 优先队列优化的 Dijkstra(边权任意值)。
  3. 状态压缩

    • 对于钥匙状态,用二进制位压缩。
    • 对于时间余数,用 t % k 表示。

七、分层图的适用场景总结

场景类型示例问题分层依据构图方式
有限次决策免费边、升级使用次数物理构图
状态依赖钥匙、时间余数钥匙状态、余数DP 构图
多维状态转移资源、权限组合状态DP 构图

八、分层图的常见错误与调试

  1. 节点编号错误:确保 u + j * n 正确映射层和节点。
  2. 层间边方向错误:层间边应单向(如从 j 层到 j+1 层)。
  3. 初始化错误dis 数组未初始化为最大值。
  4. 优先队列优先级错误:使用 greater<> 保证最小堆。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/95238.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/95238.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

当稳定币开始生息:USDT0 与 Berachain 的二次进化故事

如果说过去几年&#xff0c;稳定币是 DeFi 世界里最安稳的一块基石&#xff0c;那么 2025 年的 Berachain 正在把它们重新塑造成一种新的资产类型。在这条新兴的公链上&#xff0c;稳定币不再只是 “资金的搬运工”&#xff0c;而是摇身一变&#xff0c;成为能生息、能博弈、能…

Kafka、RabbitMQ 与 RocketMQ 在高并发场景下的高可用与性能对比分析

Kafka、RabbitMQ 与 RocketMQ 在高并发场景下的高可用与性能对比分析 消息队列作为分布式系统中常见的异步解耦组件&#xff0c;在高并发场景下对可用性和性能提出了极高的要求。本文基于生产环境需求&#xff0c;深入分析 Kafka、RabbitMQ 与 RocketMQ 三大主流消息中间件在高…

深入理解 HTTP 与 HTTPS:区别以及 HTTPS 加密原理

目录 一、HTTP 与 HTTPS 的基本概念 二、HTTP 与 HTTPS 的核心区别 三、为什么需要 HTTPS&#xff1f; 四、HTTPS 的加密通信原理&#xff08;核心&#xff09; 1. 客户端发起 HTTPS 请求 2. 服务端返回 SSL/TLS 证书 3. 客户端验证证书 4. 客户端生成对称密钥并用公钥…

零售行业的 AI 革命:从用户画像到智能供应链,如何让 “精准营销” 不再是口号?

AI 浪潮下的零售变革​在科技飞速发展的今天&#xff0c;人工智能&#xff08;AI&#xff09;正以前所未有的态势席卷全球&#xff0c;深刻地改变着各行各业的运营模式和发展轨迹&#xff0c;零售行业自然也难以置身事外。AI 技术凭借其强大的数据处理能力、精准的分析预测能力…

PyTorch 面试题及详细答案120题(96-105)-- 性能优化与调试

《前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux… 。 前后端面试题-专栏总目录 文章目录 一、本文面试题目录 96. 如何查看PyTorch模型的…

Linux 孤儿进程 (Orphan Process)

&#x1f381;个人主页&#xff1a;工藤新一 &#x1f50d;系列专栏&#xff1a;C面向对象&#xff08;类和对象篇&#xff09; &#x1f31f;心中的天空之城&#xff0c;终会照亮我前方的路 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 文章目录孤…

Linux Tun/Tap 多队列技术

&#x1f525; Linux Tun/Tap 多队列技术 引用&#xff1a;Linux tun/tap 驱动多队列模式&#xff08;C/C&#xff09; &#x1f4d6; 引言 Tun/Tap 是 Linux 内核提供的虚拟网络设备&#xff0c;广泛应用于 VPN、虚拟化、网络隧道等领域。传统单队列模式在高吞吐量场景下存…

docker 启动一个clickhouse , docker 创建ck数据库

1. 拉镜像&#xff1a;docker pull clickhouse/clickhouse-server2. 创建容器并且启动命令&#xff1a;docker run -d --name clickhouse-server \-p 8123:8123 -p 9000:9000 \clickhouse/clickhouse-server3. 日志文件的映射&#xff0c;可以自己配置下&#xff0c;目前创建的…

合约服务架构-OOP 方式

文章目录前言&#x1f3af; 经典的面向对象编程&#xff01;1. &#x1f3d7;️ **封装 (Encapsulation)**2. &#x1f9ec; **继承 (Inheritance)**3. &#x1f3ad; **多态 (Polymorphism)**4. &#x1f3a8; **抽象 (Abstraction)**&#x1f3db;️ 设计模式的应用1. **工厂…

C# 生成器模式(一个投资跟踪程序)

一个投资跟踪程序 我们考虑一个稍微简单一点的例子&#xff0c;在这个例子中&#xff0c;用一个类构造一个用户界面。假设我 们要编写一个程序来跟踪投资的效益。我们有股票、债券和基金等投资项目&#xff0c;对每一种投资项 目都要显示持有量的列表&#xff0c;这样就能够选择…

【DBCExcelConvent】CAN报文解析辅助工具之DBC与Excel互转

前言 CAN总线翻译文件DBC是整车解析过程中非常核心的一部分&#xff0c;因此为了能被各大CAN工具解析&#xff0c;它也有自己的一套编码规则。但并不是无时无刻都有条件打开该文件&#xff0c;对于工程师而言。其实比较直观和通用的大多数还是Excel表格。因此&#xff0c;为了打…

如何将iPhone日历传输到电脑

iPhone日历是i设备上一个非常出色的内置应用程序&#xff0c;可以帮助你创建、查看和管理日程或事件。对于所有iPhone用户来说&#xff0c;在iPhone日历上添加新事件非常容易。然而&#xff0c;当涉及到将日历从iPhone传输到电脑时&#xff0c;许多人可能会感到困惑&#xff0c…

TDengine 3.3.7.0 新增性能基准工具 taosgen

taosgen 工具参考手册 taosgen 是时序数据领域产品的性能基准测试工具&#xff0c;支持数据生成、写入性能测试等功能。taosgen 以“作业”为基础单元&#xff0c;作业是由用户定义&#xff0c;用于完成特定任务的一组操作集合。每个作业包含一个或多个步骤&#xff0c;并可通…

模式组合应用-组合模式

写在前面Hello&#xff0c;我是易元&#xff0c;这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误&#xff0c;欢迎大家留言指正&#xff01; 本文为设计模式间的组合使用&#xff0c;涉及代码较多&#xff0c;个人觉得熟能生巧&#xff0c;希望自己能从中学习到新…

在Ubuntu中安装配置MySql Server

1.安装MySql Server在命令行控制台执行安装命令&#xff1a;sudo apt install mysql-server安装完成后&#xff0c;因为没有root用户的密码&#xff0c;所以&#xff0c;登录不了mysql的cli。另外&#xff0c;MySql 8以上&#xff0c;lower-case-table-names默认值0&#xff0c…

Docker 40个自动化管理脚本-1 (20/40)

文章目录1. 自动化容器创建脚本2. 批量启动所有容器3. 批量停止运行中容器#!/bin/bash4. 批量删除停止的容器5. 运行容器并在退出后自动清理6. 自动重启关键容器7. 容器资源监控脚本8. 监控所有容器资源使用9. 检查所有容器日志10. 清理未使用资源脚本11. 删除悬空镜像12. 容器…

Go学习1:常量、变量的命名

golang 安装 | go-zero Documentation 在这个文档里&#xff0c;环境变量系统自动配好了&#xff08;自定义的一样&#xff09;不需要修改环境变量。 我下载的是1.25版本的。 目前使用go mod管理项目。 C的产出比太低&#xff0c;而Java和C#哲学又来源于C。 Go语言成功的项目…

2025_WSL2_Ubuntu20.04_C++20_concept 环境配置

需要使用 c20 新特性 concept 泛型约束 记录如何在 wsl2 里面配置环境&#xff0c;如果需要源工程&#xff0c;可以私发 背景&#xff1a;使用 CMakeLists.txt 配置整个工程 从官网 https://gcc.gnu.org/projects/cxx-status.html#cxx20 可以看到 concept 受 g10 支持这里注意虽…

Encoder编码器

Encoder编码器 #include <libavutil/log.h> #include <libavutil/opt.h> #include <libavcodec/avcodec.h>static int encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, FILE *out){int ret -1;ret avcodec_send_frame(ctx, frame);if(ret <…

微服务-ruoyi-cloud部署

微服务 阿里 阿里nacos 注册中心&#xff0c;配置中心 spring cloud gateway网关 公共服务 阿里sentinel 面向分布式、多语言异构化服务架构的流量治理组件 阿里seata 是一款开源的分布式事务解决方案 nginx 静态资源服器 反向代理 ruoyi-cloud部署架构 VM配置 网…