L2-001紧急救援 (dijkstra算法应用)

前提是会dijkstra算法🙂本篇会解决dijkstra大部分问题,在末尾会有一个总结

天梯赛L2-001传送门

解题思路

首先这题需要使用dijkstra算法,所以需要开三个数组

  • g[N][N]用来记录边长

  • d[N]用来记录点到源块的距离

  • st[N]用来记录是否已经被收编到源块里
    在这个基础上,还需要几个数组来应对题目

  • w[N] 用来记录最大救援队数量

  • power[N] 用来记录每个城市的救援队数量
    求最大救援队数量的思路是:
    {
    如果最短路距离更小的时候,w[j] = power[j] + w[t]
    如果距离相等,就要进行比较,找出最大的那个,用w[j] 与 w[t] + power[j] 进行比较
    }

  • num[N] 用来记录最短路数量 (最短路计数洛谷P1144 , 只不过这题要用堆优化版本)
    {
    求最短路数的思路是 :
    更新邻居的时候,如果有更小的距离,就继承t的最短路数,如图:
    最短路

    如果有相等的距离 说明有同样小的距离能到j, 就加上另一个最短路数

    }

  • pre[N] 题目要求输出路径 , 所以需要记录最短路情况

#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 1010;

int g[N][N];
int d[N];
bool st[N];
int power[N];
int num[N];
int w[N];
int pre[N];

int n,m,ori,tar;

void dijkstra()
{
    memset(d,0x3f,sizeof d);
    memset(st,0,sizeof st);
    
    d[ori] = 0;
    num[ori] = 1;
    w[ori] = power[ori];
    pre[ori] = -1;
    for(int i = 1 ; i <= n ;i++)//控制循环次数
        {
            int t = -1;
            for(int j = 0 ; j < n ; j++)//剩下的点中找最小,下标从0开始
                if(!st[j] && (t == -1 || d[j] < d[t])) t = j;
            
            st[t] = true;
            for(int j = 0 ; j < n ; j++)
                {
                    if(g[t][j] == 0x3f3f3f3f) continue;
                    if( d[t] + g[t][j] < d[j] ) 
                    {
                        d[j] = d[t] + g[t][j];
                        num[j] = num[t] ;
                        w[j] = w[t] + power[j];
                        pre[j] = t;
                    }
                    else if(d[j] == d[t] + g[t][j]) {
                        num[j] += num[t];//说明有个新的节点
                        if(w[j] < w[t] + power[j])
                        {
                            w[j] = w[t] + power[j] ;
                            pre[j] = t;
                        }
                    }
                }
        }
    cout<<num[tar]<<" "<<w[tar]<<endl;
    vector<int> trace;
    int k = tar ;
    while(k != -1)
        {
            trace.push_back(k);
            k = pre[k];
        }
    for(int i = trace.size() -1 ;i >= 0 ; i--)
        {
            cout<<trace[i];
            if(i != 0) cout<<" ";
        }
    cout<<endl;
}

int main()
{
    cin>>n>>m>>ori>>tar;
    memset(g,0x3f,sizeof g);
    for(int i = 0 ; i < n ; i++) cin>>power[i];
    
    while(m--)
        {
            int a,b,c;
            cin>>a>>b>>c;
            g[a][b] = min(g[a][b] , c);
            g[b][a] = min(g[b][a],c);
        }
    dijkstra();
    return 0;
}

总结:

  1. 由于是无向图,所以存的时候需要对称存,同时还可能有重边(就本题而言题目不严谨),所以取最小值

  2. 对于非负权边,可以不判断自环,因为自环唯一会对代码产生影响的地方是 自环松弛 , 也就是如果某个点有负权自环,才会成功进行松弛,
    当d[u] + w[u][u] < d[u]进行的时候才是自环产生影响的地方,而如果是非负权边,左边一定是大于右边的,不会进行任何更新,如果是负权边,实际上
    dijkstra算法无法处理,只能适用spfa或者bellman。

  3. 因为dijkstra算法需要n次松弛 : 每次都要找离源块最近的点,一共有n个点,所以需要松弛n次,因此外层需要一个n次循环

  4. 不要漏掉st数组的应用,每有一个点进入源块,都要进行标记,下次它不能再作为“最近的点”。

posted @ 2026-03-14 10:33  shuiwangrenjia  阅读(0)  评论(0)    收藏  举报