首页 > 其他分享 >多层图最短路问题

多层图最短路问题

时间:2024-12-30 17:21:33浏览次数:1  
标签:cnt le int 短路 多层 问题 ttop dnode dis

最短路——分层图问题


这里以一道题目为例

题目描述

Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 \(n\) 个城市设有业务,设这些城市分别标记为 \(0\) 到 \(n-1\),一共有 \(m\) 种航线,每种航线连接两个城市,并且航线有一定的价格。

Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 \(k\) 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少?

输入格式

第一行三个整数 \(n,m,k\),分别表示城市数,航线数和免费乘坐次数。

接下来一行两个整数 \(s,t\),分别表示他们出行的起点城市编号和终点城市编号。

接下来 \(m\) 行,每行三个整数 \(a,b,c\),表示存在一种航线,能从城市 \(a\) 到达城市 \(b\),或从城市 \(b\) 到达城市 \(a\),价格为 \(c\)。

输出格式

输出一行一个整数,为最少花费。

样例输入

5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100

样例输出

8

数据规模与约定

对于 \(30\%\) 的数据,\(2 \le n \le 50\),\(1 \le m \le 300\),\(k=0\)。

对于 \(50\%\) 的数据,\(2 \le n \le 600\),\(1 \le m \le 6\times10^3\),\(0 \le k \le 1\)。

对于 \(100\%\) 的数据,\(2 \le n \le 10^4\),\(1 \le m \le 5\times 10^4\),\(0 \le k \le 10\),\(0\le s,t,a,b < n\),\(a\ne b\),\(0\le c\le 10^3\)。

另外存在一组 hack 数据。


区别与一般的单图问题,这道题中我们需要考虑可以免费坐飞机的航线

相当于每个节点都存在一个状态,即到这个节点还能免费坐飞机的次数

拥有相同状态的节点共同组成一张图

所以需要对寻路的方式进行部分修改,这里采用优先队列的Dijkstra最短路算法

struct node
{
	int dw, dnode, cnt;
	bool operator<(const node& a)const {
		return dw > a.dw;
	}
};

构造优先队列 priority_queue 使用的结构体,并重载小于号

其中存储了这个节点的编号dnode ,节点到起始点的距离 dw ,以及当前节点使用的免费次数 cnt

int dis[20010][12];
bool vis[20010][12];

同样的,记录最短距离的 dis 和记录访问节点的 vis 数组都需要根据状态的不同来进行分层操作

void dijkstra(int st) {
	init();
	q.push({ 0, st, 0 });
	dis[st][0] = 0;
	while (q.size())
	{
		const node ttop = q.top();
		q.pop();
		if (vis[ttop.dnode][ttop.cnt]) continue;
		vis[ttop.dnode][ttop.cnt] = 1;
		
		for (auto it = e[ttop.dnode].begin(); it != e[ttop.dnode].end(); it++) {
			if (ttop.cnt < k&& dis[it->v][ttop.cnt + 1] > dis[ttop.dnode][ttop.cnt]) {
				dis[it->v][ttop.cnt + 1] = dis[ttop.dnode][ttop.cnt];
				q.push({ dis[it->v][ttop.cnt + 1],it->v,ttop.cnt + 1 });
			}
			if (dis[it->v][ttop.cnt] > dis[ttop.dnode][ttop.cnt] + it->w) {
				dis[it->v][ttop.cnt] = dis[ttop.dnode][ttop.cnt] + it->w;
				q.push({ dis[it->v][ttop.cnt],it->v,ttop.cnt });
			}
		}
		
	}
}

除了松弛操作外,多层图和单图的源码几乎相同,都采用了优先队列贪心来找当前最优的节点

在松弛操作内,我们需要考虑当前的节点能否使用免费的次数,免费后是不是最优

  • 当前的节点可以免费
if (ttop.cnt < k&& dis[it->v][ttop.cnt + 1] > dis[ttop.dnode][ttop.cnt]) {//判断是不是最优
	dis[it->v][ttop.cnt + 1] = dis[ttop.dnode][ttop.cnt];//如果能更新最短路径,那就更新
	q.push({ dis[it->v][ttop.cnt + 1],it->v,ttop.cnt + 1 });//将更新后的节点放入队列,注意状态也要更新
}
  • 当前的节点不能免费或不采取免费
if (dis[it->v][ttop.cnt] > dis[ttop.dnode][ttop.cnt] + it->w) {//判断能否更新
	dis[it->v][ttop.cnt] = dis[ttop.dnode][ttop.cnt] + it->w;
	q.push({ dis[it->v][ttop.cnt],it->v,ttop.cnt });//放入更新后的节点
}

Dijkstra寻路结束后,我们遍历一遍终点节点的所有状态,找到一个最小的dis[ed][i],即最优解

int ans = 1e9;
for (int i = 0; i <= k; i++) ans = min(ans, dis[ed][i]);

图上的每一个节点都有向下层的可联通节点前进的无权边(免费)

上图以 \(0\) 号节点为例,它可以通过免费次数构建出来的边向下层图移动,直到免费次数用尽

总而言之,解决分层图的关键就在建图,需要正确地通过不同的状态构造出图

完整代码:

#include <vector>
#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#include <queue>
#define streampreset() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;

struct edge
{
	int v, w;
};

struct node
{
	int dw, dnode, cnt;
	bool operator<(const node& a)const {
		return dw > a.dw;
	}
};

int n, m, k;
int st, ed;
vector<edge> e[50010];
priority_queue<node> q;
int dis[20010][12];
bool vis[20010][12];

void insert(int u, int v, int w) {
	e[u].push_back({ v,w });
}

void init(void) {
	memset(dis, 0x3f, sizeof dis);
}

void dijkstra(int st) {
	init();
	q.push({ 0, st, 0 });
	dis[st][0] = 0;
	while (q.size())
	{
		const node ttop = q.top();
		q.pop();
		if (vis[ttop.dnode][ttop.cnt]) continue;
		vis[ttop.dnode][ttop.cnt] = 1;
		for (auto it = e[ttop.dnode].begin(); it != e[ttop.dnode].end(); it++) {
			if (ttop.cnt < k&& dis[it->v][ttop.cnt + 1] > dis[ttop.dnode][ttop.cnt]) {
				dis[it->v][ttop.cnt + 1] = dis[ttop.dnode][ttop.cnt];
				q.push({ dis[it->v][ttop.cnt + 1],it->v,ttop.cnt + 1 });
			}
			if (dis[it->v][ttop.cnt] > dis[ttop.dnode][ttop.cnt] + it->w) {
				dis[it->v][ttop.cnt] = dis[ttop.dnode][ttop.cnt] + it->w;
				q.push({ dis[it->v][ttop.cnt],it->v,ttop.cnt });
			}
		}
	}
}

int main()
{
	streampreset();
	cin >> n >> m >> k >> st >> ed;
	int u, v, w;
	for (int i = 0; i < m; i++) {
		cin >> u >> v >> w;
		insert(u, v, w);
		insert(v, u, w);
	}
	dijkstra(st);
	int ans = 1e9;
	for (int i = 0; i <= k; i++) ans = min(ans, dis[ed][i]);
	cout << ans;
	return 0;
}

标签:cnt,le,int,短路,多层,问题,ttop,dnode,dis
From: https://www.cnblogs.com/dianman/p/18641765

相关文章

  • 毕设问题记录1
    今天在做毕设,需要用到一个分析二维码的库pyzbar但是在pycharm的控制台引入了之后发现用不了,显示找不到这个库搜了一下教程,猜测可能是安装到了base环境而不是毕设的anaconda环境打开了anacondaprompt(anaconda)输入condaenvlist发现一个rjb的环境,condaactivaterjb启动之......
  • 无插件直播流媒体音视频播放器EasyPlayer.js RTSP流重连问题的说明
    当前,流媒体行业正处于快速发展的阶段,全球市场规模不断扩大,技术革新持续推动行业进步。随着5G技术的推广、智能设备的更新换代,以及用户对高质量音视频内容需求的增长,流媒体技术已成为获取信息和娱乐的重要途径。那么在应用中,RTSP流重连问题是如何设计重连的?1、EasyPlayer的底层R......
  • 解决 podman 容器无法在宿主机和容器内部相互访问问题的记录
    解决podman容器无法在宿主机和容器内部相互访问问题的记录近期在使用podman时,遇到了容器无法在宿主机和容器内部相互访问的问题。经过一番探索,参考了这篇文章,成功解决了该问题。在此,我将分享解决过程及一些特别需要注意的事项。一、配置过程首先,整个操作一定要在PowerShe......
  • 关于Java的静态与非静态引起的问题
    packageStatic.non;publicclassAdd{publicintadd(inta,intb){//这里是非staticreturna+b;}publicstaticintfact(inta){//这里是staticif(a==1){return1;}else{returna*fact(a-1);......
  • uniapp使用uView2.x的自定义导航栏时,在app端出现同时两个导航栏的问题
    在使用自定义导航栏时,先是发现在h5端同时显示两个导航栏的问题.经查已成功解决,详见我的上一篇文章(在app.vue的onLoad内加上uni.hideTabBar();).但是运行到安卓真机后发现还是存在同样的情况,出现了原生底部导航栏与自定义导航栏同时出现的情况.再次经过查询得到答案,同样在a......
  • JavaScript开发中常见问题代码和相关优化Demo参考5.0
    41. 过度使用全局状态管理问题代码:在小型项目中引入了复杂的全局状态管理库(如Redux),增加了不必要的复杂性。解决方案:对于小型应用或简单状态管理需求,考虑使用React的useState和useContext,或者Vuex等框架自带的状态管理功能。//使用ReactContextAPIconstThemeContext=......
  • DataGrip 2024.3安装详细教程与激活方法(附常见问题解决)
    DataGrip概述DataGrip是一款功能强大的,适用于关系数据库和NoSQL数据库的强大跨平台工具温馨提示:本文中的方法仅供学习交流使用,如果条件允许,请支持正版软件。删除旧版本DataGrip如果您的电脑中已经安装了旧版本的DataGrip,建议首先将其完全卸载。操作步骤如下(如果未安装......
  • 如何解决数据盘扩容后磁盘空间未合并的问题?
    您好,当数据盘扩容后磁盘空间未合并时,通常是因为操作系统未能自动识别新增加的空间。以下是详细的排查步骤和解决方案:检查磁盘分区表:首先,使用命令行工具(如fdisk-l或lsblk)查看当前磁盘分区表,确认新增加的空间是否已被识别。如果看到未分配的空间,说明下一步需要进行分区扩展操作......
  • 如何解决网站无法连接本地数据库的问题?
    您好,当网站无法连接到本地数据库时,通常是由于以下几个方面的原因造成的。下面我们将详细介绍每个可能的因素及其对应的解决方案:数据库服务状态:首先要确认数据库服务是否已经启动并且正在运行。可以通过命令行工具(如servicemysqlstatus或systemctlstatusmariadb)来检查服务的......
  • 【linux-faq问题合集】clickhouse服务启动之后修改数据目录
    安装clickhouse之修改数据目录记录忘了重启服务就修改配置导致数据目录过大且不在指定位置1、现状:由于之前安装版本的问题导致有些配置文件没有同步修改/要不就是忘了重启。导致服务正常启动,但是配置文件中修改的参数未生效默认/目录磁盘一直告警,原数据目录---/var/lib/clickh......