PAT甲级真题 1003 Emergency (25分) C++实现(基于Dijkstra算法)

题目

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
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.
Output
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.
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

思路

挺难的,上来就涉及到图算法。一定要先把思路理清,伪代码写的差不多了,再动手敲。

基于Dijkstra算法,即求单源最短路径,先回顾下算法思想:

将点集V划分成两部分,S与V-S,初始S为空(也可以将起点s加入初始集,即S={s},设好初值即可),每步都把1个结点加入S。

选点的依据是,对每个结点i∈V-S,计算从s出发只经过S中结点的路径中,到达i的最短路径,记为dist[i](若不可达则记dist[i]=∞)。从所有dist[i]中选出最小值dist[j],j即是所选的点。

将j加入S后,对当前所有i∈V-S,考察经过j到达i的距离,更新dist[i]:dist[i] = min {dist[j]+e[j][i] , dist[i]}

循环直到V=S结束。到终点t的最短路径长度即为dist[t]。


在Dijkstra算法基础上,本题需额外计算最短路径数量和最大人数和。用pathCount[i]表示源点到节点i的最短路径数量,num[i]表示源点到每个节点之前的最短路径上能积累的最大救援队数量。

添加一个节点j后更新dist[i]时,若发现新的最短路径,则按以下方式更新pathCount[i]num[i]

if (newDist<dist[i]){  //有新的最短路径
    dist[i] = newDist;
    num[i] = num[j] + v[j]; //更新沿最短路径上到该点的最大积累数量
    pathCount[i] = pathCount[j]; //更新最短路径数量
}
else if (newDist==dist[i]){  //有距离相同最短路径
    num[i] = max(num[i], num[j] + v[j]); //取相同最短路径到该点累计数量中较大的
    pathCount[i] += pathCount[j];  //注意不是增加1条,而是增加了所有能到新节点的条数
}

代码中用到了INT_MAX,表示最大整数,其定义在climits.h中,该头文件中常用的常量定义还有:

INT_MAX       int 最大值
INT_MIN       int 最小值
UINT_MAX       unsigned int 最大值
UINT_MIN       unsigned int 最小值
LONG_MAX      long最大值
LONG_MIN       long最小值
FLT_MIN        float类型正数最小值
FLT_MAX       float类型正数最大值

maxmin函数则定义则在algorithm.h中。

代码

#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;

int main(){
    int n, m, c1, c2;
    cin >> n >> m >> c1 >> c2;
    vector<int> v(n);
    vector<vector<int> > e(n, vector<int>(n, INT_MAX));
    for (int i=0; i<n; i++){
        cin >> v[i];
    }
    for (int i=0; i<m; i++){
        int j, k, len;
        cin >> j >> k >> len;
        e[j][k] = len;
        e[k][j] = len;
    }
    //Dijkstra算法
    vector<int> dist(n, INT_MAX);  //从源点出发,到每个节点的最短距离
    dist[c1] = 0;
    vector<int> pathCount(n);  //从源点出发,到每个节点的最短路径数量
    pathCount[c1] = 1;
    vector<int> num(n);  //从源点出发,到每个节点之前的最短路径上能积累的最大救援队数量
    vector<bool> flag(n); //已选入确定最短路径的节点
    for (int count=0; count<n; count++){ //每次添加1个节点,添加所有节点后结束
        int minDist = INT_MAX;
        int minI = -1;
        for (int i=0; i<n; i++) {
            if (!flag[i] && dist[i]<minDist) {
                minDist = dist[i];
                minI = i;
            }
        }
        flag[minI] = true;
        for (int i=0; i<n; i++){ //考虑从新加入节点出发,更新未选节点dist值
            if (!flag[i] && e[minI][i]<INT_MAX){
                int newDist = minDist + e[minI][i];
                if (newDist<dist[i]){  //有新的最短路径
                    dist[i] = newDist;
                    num[i] = num[minI] + v[minI]; //更新沿最短路径上到该点的最大积累数量
                    pathCount[i] = pathCount[minI]; //更新最短路径数量
                }
                else if (newDist==dist[i]){  //有距离相同最短路径
                    num[i] = max(num[i], num[minI] + v[minI]); //取相同最短路径到该点累计数量中较大的
                    pathCount[i] += pathCount[minI];  //注意不是增加1条,而是增加了所有能到新节点的条数
                }
            }
        }
    }
    cout << pathCount[c2] << " " << num[c2] + v[c2] << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值