PTA刷题Advanced甲级——1003.Emergency——Day(2)

题目描述

在这里插入图片描述简单翻译一下题目:
给出N个城市,M条无向边,每个城市都有一定数目的救援小组,所有边的边权已知。现在给出起点和终点,求从起点到终点的最短路径条数及最短路径上的救援小组数目之和。如果有多条最短路径,则输出数目之和最大的。

题目分析

由于我们的起点是固定的,所以本题就是求解出从起点到终点的最短距离,很明显这是一个单源最短路径问题,即Dijkstra算法。
但是由于每一个顶点都具有一个权重,对应于题中所说的每个城市有一定数量的搜救队员。我们需要找到一条最短路径且可以搜集到最多搜救队员的路径,即这条路径上经过的点权重和最大。
标准的Dijsktra算法解题,但是我们需要开辟两个数组分别记录点权和到达某点的最大点权和。接下来就是本题中最容易忽视的点,题目要求我们找出最短路径条数而不是最短路径长度。
在这里插入图片描述
我们可以看到从V0到V2,可以有两条最短路径:1.V0->V2 2.V0->V1->V2
这两条最短路径长度都是2,样例中的答案也给出的是2,所以这会让大家产生混淆,以为题目要求我们输出的是最短路径长度。
因此,我们也需要单独开辟一个数组来记录最短路径条数,怎么更新这个数组中的值呢?
我们假设起点为s,终点为v,中介点为u,如果从s直接到达v的距离 > 从s到u再到v的距离和的话,我们就将最小距离更新,并且用num[u]来更新num[v],这条最短路径的条数应该是和从s到u的最短路径条数的相同的。
如果从s直接到达v的距离 = 从s到u再到v的距离和的话,我们就应该将num[v]+=num[u],此时又遇到了相同最短路径,条数应该+num[u]。
最终我们输出num[终点]和w[终点] (w是点权数组)。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAX = 510;//最多结点数
const int INF = 100000000;
int st,ed,n,m;//起点,终点,点数,边数
int G[MAX][MAX];//邻接矩阵
int num[MAX];//num[i]记录到达i点的最短路径条数
int weight[MAX],w[MAX],d[MAX];//点权,w[i]表示到达点i时的最大点权,d[i]表示从起点到点i的最短距离
bool vis[MAX] = {false};//记录某个结点是否被访问过,初始都未访问过
void Dijkstra(int s)//s为起点
{
    memset(num,0,sizeof(num));
    memset(w,0,sizeof(w));
    fill(d,d+MAX,INF);
    num[s] = 1;
    d[s] = 0;
    w[s] = weight[s]; 
    for(int j = 0;j < n;j++)
    {
        int u = -1;//记录当前距离最近的结点序号
        int min = INF;
        for(int i = 0;i < n;i++)
        {
            if(vis[i]==false && d[i] < min)
            {
                u = i;
                min = d[i];
            }
        }
        if(u == -1)
            return;
        vis[u] = true;
        for(int k = 0;k < n;k++)
        {
            //如果k未被访问且u可以到达k且以u为中介点可以使d[k]更优
            if(G[u][k]!=INF&&vis[k]==false)
            {
                if(d[u]+G[u][k]<d[k])
                {
                    d[k] = d[u]+G[u][k];
                    w[k] = w[u]+weight[k];
                    num[k] = num[u];
                }
                else if(G[u][k]+d[u]==d[k])
                {
                    if(weight[k]+w[u] > w[k])//以u为中介点可以形成更大的点权
                        w[k] = weight[k]+w[u];
                    num[k] += num[u];//此时遇到了一条最短长度相等的路径,要在原有基础上加上num[u]
                }
            }
        }
    }
}
int main()
{
    scanf("%d %d %d %d",&n,&m,&st,&ed);//输入边数,点数,起点和终点
    for(int i = 0;i < n;i++)
    {
        scanf("%d",&weight[i]);
    }
    int u,v;//两个相连的顶点
    fill(G[0],G[0]+MAX*MAX,INF);//初始化图G
    for(int i = 0;i < m;i++)
    {
        scanf("%d %d",&u,&v);
        scanf("%d",&G[u][v]);
        G[v][u] = G[u][v];
    }
    Dijkstra(st);
    printf("%d %d\n",num[ed],w[ed]);//最短距离条数,最短路径中的最大点权
    return 0;
}

答题用时19min
Q3——finish√

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章