【PAT甲级】1003 Emergency (25 分)

1003 Emergency

题目描述

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 Specification:

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, C​1​ 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 C​1​ to C2.

Output Specification:

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(迪杰斯特拉)算法。

对于这个算法的基本描述,可以参考百度百科,在这里简单说明一下

对于给定的图,如果不存在非负向权值的边,可以使用该算法计算从源节点到图所有其他节点的最短路径,具体方法如下

  1. 定义dis[i]数组,表示从起点到i的最短路的长度,定义visit[i]记录i节点是否被访问过,定义map[i][j]表示i节点到j节点的路径长度
  2. 初始化:
  • 首先将图信息初始化,map[i][j]记录不同节点的距离,如果是i==j,设为0,如果i,j之间不存在直接连通的路径,设为无限大INF
  • 将dis[i]设为map[start][i],即从start到i节点的长度
  • 将visit[i]设为false「包括start节点」
  1. 开始dijkstra算法核心步骤
while(1)
{
     
     
	找到未拜访过(visit[mid]==false)且dis[mid]最小的节点mid;
	if(这样的节点不存在)
		break;
	将visit[mid]设为true;
	对于所有直接跟mid连接的且未拜访过的节点i
	{
     
     
		if(直接到达i节点的最短距离dis[i]大于先到mid节点的最短距离加上从mid到i的距离)
		{
     
     
			将dis[i]更新为dis[mid]+map[mid][i]
		}
	}
}

一旦退出循环dis数组就记录了start到所有节点的最短路径长度

dijkstra算法本质上是贪心算法,每次找到一个中间节点,通过贪心的方法更改到这个中间节点到最短路径,最后整个数组就是最短路径

对于本题,不仅仅是简单的dijkstra算法,还加入了一些扩展即在满足最短路径的同时要尽可能多的增加救援队的人数【相当于最短路下的最大权重问题】

因此在while的内循环中需要增加:

if(直接到达i节点的最短距离dis[i]大于先到mid节点的最短距离加上从mid到i的距离)
{
    
    
	将dis[i]更新为dis[mid]+map[mid][i]
	将max_team[i]更新为max_team[mid]+teams[i]
}

其中max_team[i]表示到i节点可以获得的最多的救援队人数,teams[i]表示i节点有的救援队的人数

与此同时!题目的output要求输出the number of different shortest paths between C1 and C2 不是最短路的长度,而是最短路的数量!因此还需要一个num[i]数组来计算到达i的最短路的个数。

想到只有当(直接到达i节点的最短距离dis[i]等于先到mid节点的最短距离加上从mid到i的距离)证明存在多条最短路。因此再次将上述if语句更改

if(直接到达i节点的最短距离dis[i]大于先到mid节点的最短距离加上从mid到i的距离)
{
    
    
	将dis[i]更新为dis[mid]+map[mid][i]
	将max_team[i]更新为max_team[mid]+teams[i]
	将num[i]设为与num[mid]相同//因为到达i的最短路必须经过mid节点,因此到达i的最短路的个数与到达mid的最短路个数相同
}
else if(直接到达i节点的最短距离dis[i]等于先到mid节点的最短距离加上从mid到i的距离)
{
    
    
	//相同的路径长度下,只有max_team[i]比经过中间节点mid小的时候才进行更新
	if(max_team[i]<max_team[mid]+teams[i])
		将max_team[i]更新为max_team[mid]+teams[i];
	将num[i]设为num[i]+num[mid]
	//到达i的最短路个数由直接到i的或者先到mid再到i的两部分组成
}

代码

通过以上分析,我们就可以写出本题的dijkstra算法:

首先定义全局变量

#define MAXN 501
#define INF 0x3f3f3f3f

int map[MAXN][MAXN];//map[i][j]表示i到j的路径长度,INF表示没有直接相连的路
int dis[MAXN];//dis[i]表示从原点到节点i的最短路
bool visit[MAXN];//visit[i]记录i节点是否被访问过
int N,M;//存储城市个数与路径个数
int teams[MAXN];//team[i]表示i城市有的救援队的人数
int start,end;//起点城市与终点城市
int max_team[MAXN];//max_team[i]表示从原点到节点i可以获得的最大救援队人数
int num[MAXN];//num[i]表示到i节点的最短路的个数

然后对变量进行初始化

void init()
{
    
    
    int x,y;
    scanf("%d %d %d %d",&N,&M,&start,&end);
    for(int i=0;i<N;i++)
        scanf("%d",&teams[i]);
    for(int i=0;i<N;i++)//初始化两个城市间的距离,一开始所有城市间设为INF,除了自己到自己设为0
    {
    
    
        for(int j=0;j<N;j++)
        {
    
    
            if(i!=j)
            {
    
    
                map[i][j]=INF;
            }
            else
                map[i][j]=0;
        }

    }
    //记录两个城市的距离
    for(int i=0;i<M;i++)
    {
    
    
        scanf("%d %d",&x,&y);
        scanf("%d",&map[x][y]);
        map[y][x]=map[x][y];
    }
    //初始化最短路数组dis[i],设dis[i]为map[start][i]
    //初始化所有的max_team[i]为0,所有的num[i]为0,所有的visit[i]为fasle
    for (int i=0;i<N;i++)
    {
    
    
        visit[i]=false;
        dis[i]=map[start][i];
        max_team[i]=0;
        num[i]=0;
    }
    //初始化起点,将起点的max_team[start]设为起点的人数,到达起点的最短路只有一条
    max_team[start]=teams[start];
    num[start]=1;
    return;
}

调用dijkstra算法

void dijkstra()
{
    
    
    while(1)
    {
    
    
        int min=INF,index=-1;
        for(int i=0;i<N;i++)//首先找到未拜访过的距离起点最小的路径的点index
        {
    
    
            if(!visit[i])
            {
    
    
                if(min>dis[i])
                {
    
    
                    min=dis[i];
                    index=i;
                }
            }
        }
        //如果这样的点不存在,退出循环
        if(index==-1)
            break;
        visit[index]=true;//将该点的状态设为拜访过
        for(int i=0;i<N;i++)
        {
    
    
            if(map[index][i]!=INF&&!visit[i])//对于所有与index直接相连且没有拜访过的节点
            {
    
    
                if(dis[i]>dis[index]+map[index][i])//如果通过中间节点index最短路长度变短则更新
                {
    
    
                    dis[i]=dis[index]+map[index][i];//更新最短路
                    max_team[i]=max_team[index]+teams[i];//更新最大救援人数
                    num[i]=num[index];//将到达i的最短路数量设为到达index最短路数量(因为要到达i必须经过index才能最短)
                }
                else if(dis[i]==dis[index]+map[index][i])//如果相同则更新部分
                {
    
    
                    num[i]=num[i]+num[index];//最短路个数应该增加(因为两个方法到达i的最短长度相同)
                    if(max_team[i]<max_team[index]+teams[i])//只有当max_team增加了才更新
                        max_team[i]=max_team[index]+teams[i];
                }
            }
        }
    }
    return;
}

主函数如下:

int main()
{
    
    
    init();
    dijkstra();
    printf("%d %d\n",num[end],max_team[end]);
    return 0;
}

git仓库:Emergency

猜你喜欢

转载自blog.csdn.net/qq_35779286/article/details/95072660
今日推荐