天梯赛 L2-001 紧急救援 (最短路 dij)

作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。

输入格式:

输入第一行给出4个正整数N、M、S、D,其中N(2<=N<=500)是城市的个数,顺便假设城市的编号为0~(N-1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。

输出格式:

第一行输出不同的最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出首尾不能有多余空格。

输入样例:

4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2

输出样例:

2 60
0 1 3

分析:

这是单源点最短路径的题,但是与普通的单源点最短路径之间又有不同的地方,就是我们不仅要保存最短的路径,还要把这条路径上通过的每个点的军队数目的和保存下来,以及最短路径的条数,以及在这些最短路径中那条路能够获得最大的进队数目,还要把这天路径也保存下来。

所以我们需要 保存的信息有很多:

1.最短路径的条数

2.最短路径的走法

3.在这条路径上获得的军队数目和

如图:对于需要保存路径数目的最短路,如果dis[]的初始化为各个点到源点的距离的话,(源点是0 终点是3)那么3这个终点只会被松弛一次(即使从0到3距离 与 从0到1再到3 的距离相等,但是经过第二条线路所能召集的军队的数目多余0—->3,所以也要进行讨论),那么对于3来说,他的路线只有一条。

所以要把dis[]初始化为上面的表所示,这样3这个点就会被松弛两次,每松弛一次,他的路径数目就会增加。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<math.h>
#define INF 0x3f3f3f3f
using namespace std;
int N, M, S, D;
int NumArmy[505];//每个点的军队数目 
int SumArmy[505];//路径中通过当前点的进队数目和 
int map[505][505]; 
int dist[505];//源点到当前点的最短路径 
int parent[505];//路径中的前一个点 
int NumLoad[505];//路径数量 
int bj[505];//标记数组 
void dij(int s)
{
	//源点的相关变量进行初始化 
    SumArmy[s] = NumArmy[s];
    parent[s] = -1;
    NumLoad[s] = 1;
    bj[s] = 1;
    dist[s] = 0;
    //将源点到每个点的最短路径保存 
    for (int i = 0; i < N; i++)
    {
        if (map[s][i] != 0 && map[s][i] != INF)//之间有路径 
        {
            dist[i] = map[s][i];
            SumArmy[i] = NumArmy[i] + NumArmy[s];
            NumLoad[i] = 1;
            parent[i] = s;
        }
    }
    int num = N - 1;
    int op = S;
    while (num--)
    {
        int Min = INF;
        for (int i = 0; i < N; i++)
        {
            if (bj[i] == 0 && dist[i] < Min)
            {
                Min = dist[i];
                op = i;
            }
        }
        bj[op] = 1;//找到路径之外的最短距离 

        for (int i = 0; i < N; i++)
        {
            if (bj[i] == 0)
            {
                if (dist[i] > dist[op] + map[op][i])//到i点的最短路径能通过op点变化 
                {
                    dist[i] = dist[op] + map[op][i];
                    SumArmy[i] = SumArmy[op] + NumArmy[i];
                    NumLoad[i] = NumLoad[op];
                    parent[i] = op;
                }
                else //else不能忘,找bug找了好久 
				if (dist[i] == dist[op] + map[op][i])//虽然路径长度不能变化,可以比较军队数目来改变路径 
                {
                    NumLoad[i] = NumLoad[i] + NumLoad[op];
                    if (SumArmy[i] < SumArmy[op] + NumArmy[i])
                    {
                        SumArmy[i] = SumArmy[op] + NumArmy[i];
                        parent[i] = op;
                    }
                }
            }
        }
    }
}
void Shuchu(int D)
{
    if (parent[D] == -1)
    {
        printf("%d", D);
        return ;
    }
    else
    {
        Shuchu(parent[D]);
        printf(" %d", D);
    }
}
int main()
{
    scanf("%d%d%d%d", &N, &M, &S, &D);
    for (int i = 0; i < N; i++)
    {
        scanf("%d", &NumArmy[i]);
        //初始化 
        dist[i] = INF;
        bj[i] = 0;
        NumLoad[i] = 0;
        SumArmy[i] = 0;
    }
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++)
        {
            if (i == j)
                map[i][j] = 0;
            else
                map[i][j] = INF;
        }
    int n1, n2, n3;
    for (int i = 0; i < M; i++)
    {
        scanf("%d%d%d", &n1, &n2, &n3);
        map[n1][n2] = map[n2][n1] = min(n3, map[n1][n2]);
    }
    dij(S);
    printf("%d %d
", NumLoad[D], SumArmy[D]);
    Shuchu( D);
    return 0;
}
原文地址:https://www.cnblogs.com/cmmdc/p/6729668.html