1003 Emergency (25分)
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, C1 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 C1 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的扩展(Dijkstra算法具有良好的可扩展性)。在找到最小路径的同时还要找到路径条数和结点权值和的最大值。
使用weight[ ]保存每个节点的权值,w[ ]记录路径权重之和,sum[ ]记录路径条数。
更新思路:算法的基本框架还是逃不出Dijkstra,但是要在这之上根据题目的要求填填补补。
当dis[u] + e[u][v] < dis[v]时:意味着需要当前未标记的最小路径结点建立新的路,换句话说就是不会产生新的路,sum不变,weight累加。
当dis[u] + e[u][v] == dis[v]时:意味着出现了岔路,由算法的更新特质可以知道,前一路线的所有信息一定全部被更新完成,所以路线数sum需要更新,weight需要则有选择。
代码如下
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, c1, c2; //weight数组用来保存初始结点权重, dis用来保存每个顶点到起点的最短距离(对dis的操作方法,保证了可以松弛每一条边)
int e[510][510], weight[510], dis[510], num[510], w[510];//num保存了道路数目, w保存了到当前结点的累计权重
bool visit[510];
const int inf = 99999999;
int main()
{
cin >> n >> m >> c1 >> c2;
for (int i = 0; i < n; i++) cin >> weight[i];
fill(e[0], e[0] + 510 * 510, inf);
fill(dis, dis + 510, inf);
int a, b, c;
for (int i = 0; i < m; i++) { cin >> a >> b >> c; e[a][b] = e[b][a] = c; } //使用邻接矩阵保存信息
dis[c1] = 0;
w[c1] = weight[c1];
num[c1] = 1;
//visit[c1] = true; 在最开始不可以初始化visit[c1]
for (int i = 0; i < n; i++) //单元最短路,循环n次,完成对所有边的松弛
{
int u = -1, min = inf;
for (int j = 0; j < n; j++) //首先遍历dis数组,找到没有访问过的距离起点最近最近结点
{
if (!visit[j] && min > dis[j])
{
u = j;
min = dis[j];
}
}
if (u == -1) break; //当所有结点都被访问过,算法完成
visit[u] = true;
for (int v = 0; v < n; v++)
{
if (visit[v] == false && e[u][v] != inf) //从当前最优结点松弛未到达但是可以到达的结点(已到达的结点一定就是最小值)
{
if (dis[u] + e[u][v] < dis[v]) //可以松弛时
{
dis[v] = dis[u] + e[u][v];
num[v] = num[u]; //继承数目
w[v] = w[u] + weight[v]; //添加结点权重
}
else if (dis[u] + e[u][v] == dis[v]) //长度一样时
{
num[v] = num[v] + num[u]; //由于两边都可以到达,累计步数
if (w[u] + weight[v] > w[v]) //判断当前到达的情况是否优于上次到达
w[v] = w[u] + weight[v]; //如果是,更新
}
}
}
}
printf("%d %d", num[c2], w[c2]);
return 0;
}