首页 > 其他分享 >P4568 [JLOI2011] 飞行路线

P4568 [JLOI2011] 飞行路线

时间:2024-06-09 10:11:14浏览次数:14  
标签:node current layer dist JLOI2011 int P4568 next 路线

题目 P4568 [JLOI2011]飞行路线 要求找到在最多可以免费乘坐k条航线的情况下,从城市s到城市t的最少花费。这是一个典型的分层图问题。

分层图的建模

1. 建立层次

将原图分成k+1层,表示在0到k次免费乘坐的情况下的状态。第i层表示已经使用了i次免费乘坐机会的状态。

2. 建立节点和边

每层图的节点和原图的节点对应,每层之间通过边相连:

  • 同层边: 从第i层的一个节点到同一层的另一个节点,边权保持不变。
  • 跨层边: 从第i层的一个节点到下一层(第i+1层)的对应节点,边权设为0,表示使用一次免费乘坐机会。

这样,每个节点(i, v)表示在使用了i次免费乘坐机会时在节点v。

算法步骤

1. 初始化

创建一个距离数组distdist[i][j]表示在使用了i次免费机会后到达节点j的最短距离。初始时,所有值设为无穷大,起点的距离为0。

vector<vector<int>> dist(k + 1, vector<int>(n, INF));
dist[0][s] = 0;

2. 优先队列

使用优先队列(最小堆)来存储待处理的节点,每个节点存储当前距离、层次和节点编号。

priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<tuple<int, int, int>>> pq;
pq.push({0, 0, s});

3. 处理节点

从优先队列中取出距离最小的节点,对其进行松弛操作:

  • 同层松弛: 更新同一层的邻接节点的距离。
  • 跨层松弛: 更新下一层的对应节点的距离。
while (!pq.empty()) {
    auto [current_distance, current_layer, current_node] = pq.top();
    pq.pop();
    
    // 同层松弛
    for (const auto& edge : graph[current_node]) {
        int next_node = edge.to;
        int weight = edge.weight;
        if (dist[current_layer][current_node] + weight < dist[current_layer][next_node]) {
            dist[current_layer][next_node] = dist[current_layer][current_node] + weight;
            pq.push({dist[current_layer][next_node], current_layer, next_node});
        }
    }
    
    // 跨层松弛
    if (current_layer < k) {
        for (const auto& edge : graph[current_node]) {
            int next_node = edge.to;
            if (dist[current_layer][current_node] < dist[current_layer + 1][next_node]) {
                dist[current_layer + 1][next_node] = dist[current_layer][current_node];
                pq.push({dist[current_layer + 1][next_node], current_layer + 1, next_node});
            }
        }
    }
}

4. 找最小值

在最后的所有层次中找到到达终点t的最小值。

int result = INF;
for (int i = 0; i <= k; ++i) {
    result = min(result, dist[i][t]);
}

示例代码

以下是完整的C++代码实现:

#include <iostream>
#include <vector>
#include <queue>
#include <tuple>
#include <limits>

using namespace std;

const int INF = numeric_limits<int>::max();

struct Edge {
    int to;
    int weight;
};

int main() {
    int n, m, k, s, t;
    cin >> n >> m >> k >> s >> t;
    vector<vector<Edge>> graph(n);
    
    for (int i = 0; i < m; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        graph[u].push_back({v, w});
        graph[v].push_back({u, w});
    }
    
    vector<vector<int>> dist(k + 1, vector<int>(n, INF));
    dist[0][s] = 0;
    
    priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<tuple<int, int, int>>> pq;
    pq.push({0, 0, s});
    
    while (!pq.empty()) {
        auto [current_distance, current_layer, current_node] = pq.top();
        pq.pop();
        
        if (current_distance > dist[current_layer][current_node]) continue;
        
        for (const auto& edge : graph[current_node]) {
            int next_node = edge.to;
            int weight = edge.weight;
            if (dist[current_layer][current_node] + weight < dist[current_layer][next_node]) {
                dist[current_layer][next_node] = dist[current_layer][current_node] + weight;
                pq.push({dist[current_layer][next_node], current_layer, next_node});
            }
        }
        
        if (current_layer < k) {
            for (const auto& edge : graph[current_node]) {
                int next_node = edge.to;
                if (dist[current_layer][current_node] < dist[current_layer + 1][next_node]) {
                    dist[current_layer + 1][next_node] = dist[current_layer][current_node];
                    pq.push({dist[current_layer + 1][next_node], current_layer + 1, next_node});
                }
            }
        }
    }
    
    int result = INF;
    for (int i = 0; i <= k; ++i) {
        result = min(result, dist[i][t]);
    }
    
    cout << result << endl;
    return 0;
}

这个代码实现了从起点到终点的最短路径计算,考虑了最多k次的免费乘坐机会.

接下来介绍分层图

什么是分层图Dijkstra算法?

想象一下,你在一个大房子里,每一层楼代表不同的阶段或状态。在这个房子里,你可以从一个房间走到另一个房间,也可以坐电梯到不同的楼层。我们需要找到从一个房间到另一个房间的最短路径,但有一个特别的优惠:你最多可以免费坐几次电梯。

分层图的解释

  1. 楼层和房间

    • 房子的每一层楼代表一个状态,每层楼有很多房间。
    • 你在某一层的某个房间,想要去另一层的另一个房间。
  2. 路径和电梯

    • 在同一层楼里走路(普通路径),每走一步需要花费时间(边权重)。
    • 坐电梯到另一层,可以免费(跨层边)。

分层图Dijkstra算法怎么做?

1. 初始化

首先,我们要记下每个房间到达的最短时间。开始时,我们不知道去哪里需要多少时间,所以把每个房间的时间都设为很大很大的数(无穷大)。但是我们知道从自己开始的房间出发时间是0。

2. 使用优先队列

我们用一个特殊的队列来帮我们记录哪些房间需要处理,先处理最容易到达的房间。这个队列就像一个排序的清单,每次我们从中取出最靠前的房间来处理。

3. 处理房间

每次我们从队列中取出一个房间,然后看看从这个房间出发,能走到哪些其他房间:

  • 同层楼房间:如果你在同一层楼走到另一个房间,就更新这个房间的最短时间。
  • 跨层电梯:如果你坐电梯到另一层楼的房间,免费,但使用一次免费机会。

4. 找到最短路径

最后,当所有房间都处理完,我们找到最小的时间值,就是从起点到终点的最短路径时间。

示例

假设我们有一个两层的房子,每层楼有三个房间:

  • 楼层0

    • 房间0到房间1的时间是2
    • 房间0到房间2的时间是4
    • 房间1到房间2的时间是1
  • 楼层1

    • 房间0到房间1的时间是3
    • 房间1到房间2的时间是1
  • 跨层电梯

    • 从楼层0的房间0到楼层1的房间1的时间是0(免费)

我们想从楼层0的房间0到楼层1的房间2,最多可以免费坐一次电梯。我们就可以用分层图Dijkstra算法来计算最短时间。

C++代码示例

#include <iostream>
#include <vector>
#include <queue>
#include <tuple>
#include <limits>

using namespace std;

const int INF = numeric_limits<int>::max();

struct Edge {
    int to;
    int weight;
};

int main() {
    int n = 3, m = 5, k = 1, s = 0, t = 2;
    vector<vector<Edge>> graph(n);
    
    // 图的边
    graph[0].push_back({1, 2});
    graph[0].push_back({2, 4});
    graph[1].push_back({2, 1});
    graph[0].push_back({1, 1});  // 跨层电梯

    vector<vector<int>> dist(k + 1, vector<int>(n, INF));
    dist[0][s] = 0;
    
    priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<tuple<int, int, int>>> pq;
    pq.push({0, 0, s});
    
    while (!pq.empty()) {
        auto [current_distance, current_layer, current_node] = pq.top();
        pq.pop();
        
        if (current_distance > dist[current_layer][current_node]) continue;
        
        for (const auto& edge : graph[current_node]) {
            int next_node = edge.to;
            int weight = edge.weight;
            if (dist[current_layer][current_node] + weight < dist[current_layer][next_node]) {
                dist[current_layer][next_node] = dist[current_layer][current_node] + weight;
                pq.push({dist[current_layer][next_node], current_layer, next_node});
            }
        }
        
        if (current_layer < k) {
            for (const auto& edge : graph[current_node]) {
                int next_node = edge.to;
                if (dist[current_layer][current_node] < dist[current_layer + 1][next_node]) {
                    dist[current_layer + 1][next_node] = dist[current_layer][current_node];
                    pq.push({dist[current_layer + 1][next_node], current_layer + 1, next_node});
                }
            }
        }
    }
    
    int result = INF;
    for (int i = 0; i <= k; ++i) {
        result = min(result, dist[i][t]);
    }
    
    cout << result << endl;
    return 0;
}

通过这个例子,你可以看到我们如何一步一步地找到最短路径。这种方法在有很多状态和路径选择时非常有用。希望这个解释能帮助你更好地理解分层图Dijkstra算法!

标签:node,current,layer,dist,JLOI2011,int,P4568,next,路线
From: https://www.cnblogs.com/luyoulingoi/p/18239286

相关文章

  • 光通信技术路线
    自从1947年Bell实验室诞生第一支晶体管以来,芯片的尺寸大小和晶体管的集成度都遵循着“摩尔定律”进行飞速的发展。然而摩尔定律随着芯片尺寸的减小,进入到深亚微米或纳米量级之后,其发展也面临越来越严峻的挑战。近几年来虽然芯片上的晶体管的数量仍在增加,但是由于晶体管的尺寸不......
  • 【2024最新】Python 学习路线分享
    学习资料已打包,需要的小伙伴可以戳这里学习资料整理了一份Python学习路线。内容依然是从入门到进阶,既有教程,也有经典书籍推荐,还有实战开源项目。Python的发展方向还是挺多的,比如服务端开发,爬虫,数据分析,机器学习等,本文推荐的内容全部是服务端开发,Web开发方向。主......
  • §3. 格林公式、曲线积分与路线的无关性
    掌握格林公式及其应用(将第二型曲线积分与二重积分联系起来,在计算时可以相互转化)。掌握单连通区域的概念,以及曲线积分与路径无关的判别和应用。难点:格林公式中的条件是必需的,否则结论不能成立。注意例2和215页中间一段例子的区别(是否包含原点)。重点习题:例1-例4经典方法:将二重积......
  • 这才是CSDN最系统的网络安全学习路线(建议收藏)
    01什么是网络安全网络安全可以基于攻击和防御视角来分类,我们经常听到的“红队”、“渗透测试”等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。无论网络、Web、移动、桌面、云等哪个领域,都有攻与防两面性,例如Web安全技术,既有Web渗透,也有W......
  • 大模型快速入门+学习路线
    什么是大模型大模型,是指在人工智能领域,特别实在自然语言处理和机器学习中,拥有大量参数的深度学习模型。这些模型通过在大规模数据集上进行训练,能够学到丰富的数据表示和模式,从而在各种任务上表现出色,如文本生成,语言理解,图像识别等。大模型是具有大量参数和复杂结构的模型,这些模......
  • Cocos Creator开发学习路线
    1.JavaScript与TypeScript程序设计由于可以跨平台发布,同时要能支持h5的游戏,cocoscreator选择了JavaScript与TypeScript来做为它的开发语言,所以我们要先学习JavaScript与TypeScript。TypeScript是基于JavaScript的一个语法糖,运行的时候被编译为JavaScript,所以我们要先学JavaS......
  • 2024年网络安全学习指南!详尽路线图,从零基础到黑客高手的进阶之路!
    零基础小白,到就业!入门到入土的网安/黑客学习路线!建议的学习顺序:一、网络安全学习普法(心里有个数,要进去坐几年!)1、了解并介绍《网络安全法》2、《全国人大常委会关于维护互联网安全的决定》3、《中华人民共和国计算机信息系统安全保护条例(2011年修正)》4、《中华人民共......
  • 蓝桥杯-AB路线(详细原创)
    问题描述:有一个由N×M个方格组成的迷宫,每个方格写有一个字母A或者B。小蓝站在迷宫左上角的方格,目标是走到右下角的方格。他每一步可以移动到上下左右相邻的方格去。由于特殊的原因,小蓝的路线必须先走K个A格子、再走K个B格子、再走K个A格子、再走K个B格子......
  • 购买课程,钱花销的一些问题(更新五天中go的路线)
    我觉得省点小钱把精力留给需要精力的事情是很有必要的,但是我认为需要掌握一定的决策技巧,虽然决策只能是相对完美受限经验,但不断增进知识可以家加深我们的理解这门课程:gogormgin博客rabbitmqprometheusdockeretcdgozerok8s面试题(后续其他技术随便找门课看看不一样吗)优......
  • Java学习路线思维导图
    目录Java学习流程1.学习大纲2.Java开发中常用的DOS命令Java入门学习思维导图Java学习流程通过大纲了解学习的重点,通过目录依次深入【注:Java环境的搭建百度,提升自己百度的能力】1.学习大纲学习流程如下:Java基础语法Java的发展与特点(了解)Java语言规范(精......