PAT 1003 迪杰斯特拉算法详解

文章通过一个紧急救援团队队长接到救援任务的场景,介绍了如何使用迪杰斯特拉算法来找到城市间最短路径并计算能召集的最大救援力量。文章提供了输入输出规格、示例输入输出,以及算法的步骤和代码实现,强调了算法的核心在于纳新和传销的过程,即寻找最近节点并更新路径和资源信息。
摘要由CSDN通过智能技术生成

迪杰斯特拉算法

万物基于传销——硬核的半佛仙人

题目是PAT的题目,Advanced Level 1003 Emergency

代码是github的代码,原仓库地址https://github.com/liuchuo/PAT

1003 Emergency 紧急事件

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

作为一名城市的紧急救援队队长,你被赋予了一张特殊的国家地图。地图显示了几个分散的城市,这些城市之间由一些道路相连。地图上标注了每个城市的救援队数量以及任意两个城市之间道路的长度。当其他城市发出紧急呼叫时,你的工作是尽快带领你的队员前往现场,并在途中尽可能地召集更多的援助。

Input Specification:

Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (≤500) - the number of cities (and the cities are numbered from 0 to N−1), M - the number of roads, C1 and C2 - the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1, c2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1 to C2.

输入格式:

每个输入文件包含一个测试用例。对于每个测试用例,

第一行包含4个正整数:N (≤500) - 城市数量(城市编号从0到N−1),M- 道路数量,C1和C2 - 当前所在城市和必须救援的城市。

下一行包含N个整数,其中第i个整数是第i个城市的救援队数量。

然后是M行,每行描述一条道路,其中包含三个整数c1,c2和L,分别是连接一条道路的城市对和该道路的长度。

保证存在至少一条从C1到C2的路径。

Output Specification:

For each test case, print in one line two numbers: the number of different shortest paths between C1 and C2, and the maximum amount of rescue teams you can possibly gather. All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.

输出格式:

对于每个测试用例,一行输出两个数字:C1和C2之间不同的最短路径数和最多可能聚集的救援队数量。

每行中所有数字之间必须恰好有一个空格,行末不能有多余空格。

Sample Input:

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

请添加图片描述

Sample Output:

2 4

弄明白题目后,学过数据结构的我们很容易想到,这就是一个最短路径规划,可以使用迪杰斯特拉算法求解。

然而,本人太菜了,书到用时方恨少,只知用什么,不知怎么用。但看完题解,恍然大悟,总结了迪杰斯特拉算法的套路,给大家做个分享。先看题解。

solution:

#include <bits/stdc++.h>
using namespace std;
int n, m, c1, c2;
//dis C1到达各结点累计路程
//num 到达指定结点最短路径数
//w 累计aid表 
int path[510][510], aid[510], dis[510], num[510], w[510];
//visit访问标志(已确定最短路径的点)
bool visit[510];
const int inf = 99999999;
int main() {
    //****************************************
    //输入
    scanf("%d%d%d%d", &n, &m, &c1, &c2);
    for(int i = 0; i < n; i++)
        scanf("%d", &aid[i]);
    
	//道路信息存储在path图中,且无道路为无穷大
	fill(path[0], path[0] + 510 * 510, inf);
    int a, b, c;
    for(int i = 0; i < m; i++) {
        scanf("%d%d%d", &a, &b, &c);
        path[a][b] = path[b][a] = c;
    }
    //******************************************
    //迪杰斯特拉算法
    
    //step1初始化
    //1.dis C1到达各结点累计路程
    //都不可达
    fill(dis, dis + 510, inf);
    //源点可达,置为0
    dis[c1] = 0;
    //2.w 累计救援数
    //源点救援数为本身救援数
    w[c1] = aid[c1];
    //3.num 到达指定结点最短路径数
    //到达源点就1条路
    num[c1] = 1;
    
    //step2遍历图 
    //每次循环目的:
    //1.纳新-接纳一个最小路径外的最近结点
    //2.传销-让新成员发展下线
    for(int i = 0; i < n; i++) {
    	//1.纳新
        //要求结点未在最短路径内且距离C1最近
        //u 拟纳新的结点下标
        //minn 该结点距离C1的距离
        int u = -1, minn = inf;
        for(int j = 0; j < n; j++) {
        	//结点未在最短路径内且距离C1最近
            if(visit[j] == false && dis[j] < minn) {
                u = j;
                minn = dis[j];
            }
        }
        //如果无新可纳,就结束吧
        if(u == -1) break;
        //纳新后得发徽章
        visit[u] = true;
        
        //2.传销
        //让新成员发展下线
        for(int v = 0; v < n; v++) {
        	//结点未在最短路径内且该结点与新成员之间存在通路
            if(visit[v] == false && path[u][v] != inf) {
            	//如果 新成员到达该点的距离 比 之前的距离 短
                //1.更新累计距离
                //2.更新到达该结点最短路径数
                //3.更新累计救援数(直接累加)
                if(dis[u] + path[u][v] < dis[v]) {
                    dis[v] = dis[u] + path[u][v];
                    num[v] = num[u];
                    w[v] = w[u] + aid[v];
                }
                //如果 新成员到达该点的距离 与 之前的距离 相等
                //1.更新到达该结点最短路径数(两条支路累加)
                //3.更新累计救援数(需要比较两条支路)
				else if(dis[u] + path[u][v] == dis[v]) {
                    num[v] = num[v] + num[u];
                    if(w[u] + aid[v] > w[v])
                        w[v] = w[u] + aid[v];
                }
            }
        }
    }
    //******************************************
    //输出
    printf("%d %d", num[c2], w[c2]);
    return 0;
}

模板总结:

#include <bits/stdc++.h>
using namespace std;
//一、定义变量
//变量依据题目自行添加

//path 邻接矩阵
//dis 指定点到达各结点累计路程
int path[N][N], dis[N];
//visit访问标志(已确定最短路径的点)
bool visit[N];
//定义最大值
const int inf = 99999999;

int main() {
    //****************************************
    //二、输入
	//1.邻接矩阵path存储
    //2.源结点C1,到达结点C2
    //3.其他
    
    //******************************************
    //三、迪杰斯特拉算法
    
    //step1初始化
    //1.dis C1到达各结点累计路程
    //都不可达
    fill(dis, dis + 510, inf);
    //源点可达,置为0
    dis[c1] = 0;
    //2.其他
    //根据题目自行决定
	
    
    //step2遍历图
    //每次循环目的:
    //1.纳新-接纳一个最小路径外的最近结点
    //2.传销-让新成员发展下线
    for(int i = 0; i < size; i++) {
    	//1.纳新
        //要求结点未在最短路径内且距离源结点最近
        //u 拟纳新的结点下标
        //minn 该结点距离C1的距离
        int u = -1, minn = inf;
        for(int j = 0; j < n; j++) {
        	//结点未在最短路径内且距离C1最近
            if(visit[j] == false && dis[j] < minn) {
                u = j;
                minn = dis[j];
            }
        }
        //如果无新可纳,就结束吧
        if(u == -1) break;
        //纳新后得发徽章
        visit[u] = true;
        
        //2.传销
        //让新成员发展下线
        for(int v = 0; v < n; v++) {
        	//结点未在最短路径内且该结点与新成员之间存在通路
            if(visit[v] == false && path[u][v] != inf) {
            	//如果 新成员到达该点的距离 比 之前的距离 短
                //1.更新累计距离
                //2.其他
                if(dis[u] + path[u][v] < dis[v]) {
                    dis[v] = dis[u] + path[u][v];
                  	//根据题目自行添加
                }
                //如果 新成员到达该点的距离 与 之前的距离 相等
                //1.根据题目自行添加
				else if(dis[u] + path[u][v] == dis[v]) {
                    ;
                }
            }
        }
    }
    
    //******************************************
    //输出
    printf("%d %d", num[c2], w[c2]);
    return 0;
}

技术总结:

...
//一、定义变量
//变量依据题目自行添加

//path 邻接矩阵
//dis 指定点到达各结点累计路程
//visit访问标志(已确定最短路径的点)
//定义最大值

int main() {
    //****************************************
    //二、输入
	//1.邻接矩阵path存储
    //2.源结点C1,到达结点C2
    //3.其他
    
    //******************************************
    //三、迪杰斯特拉算法
    
    //step1初始化
    //1.dis C1到达各结点累计路程
    //都不可达,置为inf
    //源点可达,置为0
    //2.其他
    //根据题目自行决定
	
    //step2遍历图
    //每次循环目的:
    //1.纳新-接纳一个最小路径外的最近结点
    //2.传销-让新成员发展下线
    //循环遍历
    	//1.纳新
        //要求结点未在最短路径内且距离源结点最近
        //u 拟纳新的结点下标
        //minn 该结点距离C1的距离
        //循环遍历
        	//结点未在最短路径内且距离C1最近
            //目前暂定就它了
        //如果无新可纳,就结束吧
        //纳新后得发徽章

        //2.传销
        //让新成员发展下线
        //循环遍历
        	//结点未在最短路径内且该结点与新成员之间存在通路
            	//如果 新成员到达该点的距离 比 之前的距离 短
                //1.更新累计距离
                //2.其他
                //如果 新成员到达该点的距离 与 之前的距离 相等
                //1.根据题目自行添加
    
    //******************************************
    //输出

}

去掉代码,保留注释,是不是很清晰。

一句话就是:转销纳新、发展下线,要求距离老大最近的。

好多牛逼的方法都基于传销。我爱这个魔幻的世界。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Z天南之城Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值