L2-001 紧急救援

唯手熟尔,路还很长。。。

#include<bits/stdc++.h> 不建议使用万能头文件
// #include<iostream>
// #include<set>
// #include<cstring>
// #include<queue>
#define ll long long; // 定义long long类型
using namespace std;
const int inf = 0x3f3f3f3f;//无穷大
typedef pair<int,int>PII;
int n, m, s, d;                       //dis[i]表示当前起点到i点的最短距离
int mp[510][510], vis[510], dis[510];//vis数组的作用是记录每个节点是否已被访问过。在Dijkstra算法中,每次从队列中取出距离起点最短的节点时,
//需要判断该节点是否已经被访问过。若该节点已被访问过,则可以直接跳过,因为它的最短路径已经求得,不需要再次计算。因此,vis数组可以避免重复计算,提高程序的效率。
int total[510], city[510], path[510], road[510];//road记录从起点到每个节点的最短路径数量,path[i]表示i节点的最短前驱节点,total[i] 表示从起点到i节点的最短路径的救援人员数总和

void dijkstra()
{
    memset(dis, 0x3f, sizeof(dis)); // 初始化距离为无穷大
    total[s] = city[s]; // 初始点的总点权值为其本身的点权值
    path[s] = -1; // 初始点没有前驱节点
    road[s] = 1; // 初始点到达方式为一种
    dis[s] = 0; // 起点到起点的距离为0
    //采用优先队列的路线是:先摸出第一条最短路径,(因为这道题还需要找相同距离的可能以及相同距离中救援队最多的情况)所以还需要依次再摸出第二条第三条...的最短路径
    //也就是说最短路径的下一个最短节点会被优先访问
    priority_queue<PII>q;//优先队列当存储的是一组数据时默认第一个数据是权值,存储的是pair类型
    q.push({ 0,s });// 将起点加入队列
    while (q.size())
    {
        int now = q.top().second; // 取出队首元素的节点
        q.pop(); // 删除队首元素
        if (vis[now]) // 若当前节点已被访问过,则跳过
            continue;
        vis[now] = 1; // 标记当前节点已被访问
        for (int i = 0; i < n; i++)
        {
            if (mp[now][i])//(判断now到i是否存在路径)用now节点去访问所有的节点来找到最短路径
            {
                if (dis[i] > dis[now] + mp[now][i]) // (dis[i]在i被访问前都是无穷大)若通过当前节点到达其它节点更近,则更新信息
                {
                    path[i] = now; // 更新前驱节点(i的前驱节点为now)
                    total[i] = total[now] + city[i]; // 更新总点权值(到i的救援队总数为now的总和+城市i的数量)
                    dis[i] = dis[now] + mp[now][i]; // 更新距离
                    road[i] = road[now]; // ***更新到达方式到达次数以更短的为准
                    if(i!=d)
                        q.push({ -dis[i],i }); // 将更新后的节点加入队列中 (采用负数是因为希望最近的被优先处理,使得更小的距离对应更高的优先级。)
                }
                else
                {
                    if (dis[i] == dis[now] + mp[now][i]) // 若路径相同,则更新路径数量和总点权值
                    {
                        //也就是说在 "now到i的距离mp[now][i]+now的最短距离dis[now]等于i的最短距离dis[i]" 的前提下
                        //从起点到i的新的最短路径数量等于 起点到i的最短路径数road[i]加上起点到now的最短路径数road[now]
                        //最短路径树的延长(可以画张图理解)
                        road[i] += road[now]; // ***更新路径数量
                        if (total[i] < total[now] + city[i]) // 若当前节点的总点权值更大,则更新
                        {
                            total[i] = total[now] + city[i];
                            path[i] = now;
                        }//有一点最大的可能都不放弃||^||
                    }
                }
            }
        }
    }
}

int main()
{
    cin >> n >> m >> s >> d;
    for (int i = 0; i < n; i++)
        cin >> city[i]; // 输入每个城市的点权值
    for (int i = 0; i < m; i++)
    {
        int x, y;
        cin >> x >> y;
        cin >> mp[x][y];
        mp[y][x] = mp[x][y]; // 无向图,因此需要将两个方向的边权值都赋值
    }

    dijkstra(); // 调用Dijkstra算法求解最短路径
    int now = d;
    vector<int>answer; // 存储最短路径
    while (now != -1)
    {
        answer.push_back(now);
        now = path[now]; // 逆序遍历路径,找到所有经过的节点
    }//注意了注意了其对应记录的是前驱节点即对应必须逆序遍历
    cout << road[d] << " " << total[d] << endl; // 输出到达终点的路径数量和总点权值

    for (int i = (int)answer.size() - 1; i > 0; i--)
        cout << answer[i] << " ";
    cout << answer[0]; // 输出从起点到终点的最短路径

    return 0;
}

  • 34
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值