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.
Each input file contains one test case. For each test case, the first line contains 4 positive integers: N ( ≤ 500 ) N (≤500) N(≤500) - the number of cities (and the cities are numbered from 0 0 0 to N − 1 ) N−1) N−1), M - the number of roads, C 1 C_1 C1 and C 2 C_2 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 c 1 c_1 c1, c 2 c_2 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 C_1 C1 to C 2 C_2 C2
For each test case, print in one line two numbers: the number of different shortest paths between C 1 C_1 C1 and C 2 C_2 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.
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
2 4
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
/**
* @create-date 2019-07-27 19:01
*/
public class Main {
// ====================================================
// 测试用例一
// 5 6 0 4
// 1 2 1 5 3
// 0 1 1
// 0 2 2
// 0 3 1
// 1 2 1
// 2 4 1
// 3 4 1
// 测试用例二
// 4 5 0 3
// 1 2 3 4
// 0 1 1
// 0 2 1
// 1 3 1
// 2 3 1
// 0 3 2
// ====================================================
public static void main(String[] args) throws IOException {
// 初始化输入
Reader.init(System.in);
// 获取到城市的数量
int cities = Reader.nextInt();
// 获取路的条数
int roads = Reader.nextInt();
// 获取到起始城市下标
int startIndex = Reader.nextInt();
// 获取到终点城市下标
int destinationIndex = Reader.nextInt();
// 存储每个城市的治疗小组
int[] citiesPerson = new int[cities];
// 创建一个 cities * cities 的数组,存储城市与城市之间的道路的联系
int[][] map = new int[cities][cities];
// 厨师换城市之间的关系,
for (int[] m : map) {
Arrays.fill(m, 0XFFFF);
}
// 获取城市人数
for (int i = 0; i < cities; i++) {
citiesPerson[i] = Reader.nextInt();
}
// 是一个无向图
for (int i = 0; i < roads; i++) {
int start = Reader.nextInt();
int end = Reader.nextInt();
int distance = Reader.nextInt();
// 是一个无向图
map[start][end] = distance;
map[end][start] = distance;
}
// 通过 dijkstra 算法获取到结果
Vector<Vector<Integer>> paths = dijkstra(map, startIndex);
{
// 获取到路径条数
int count = count(paths, destinationIndex);
// 获取到最多的治疗小组
int maxGroupCount = citiesPerson[destinationIndex] + filter(paths, destinationIndex, citiesPerson);
System.out.println(count + " " + maxGroupCount);
}
}
/**
* 获取不同路径的条数
*
* @param paths 路径
* @param end 终点下标
* @return 道路的条数
*/
public static int count(Vector<Vector<Integer>> paths, int end) {
int count = 0;
Vector<Integer> vector = paths.get(end);
if (vector.isEmpty()) {
count = 1;
}
for (int temp : vector) {
count += count(paths, temp);
}
return count;
}
/**
* 过滤出想要的结果
*
* @param paths 路径向量
* @param end 终点
* @param citiesPerson 每个城市的人数
* @return 返回最大的结果
*/
private static int filter(Vector<Vector<Integer>> paths, int end, int[] citiesPerson) {
int max = 0;
// 取出 end 对应的向量
Vector<Integer> vector = paths.get(end);
for (int temp : vector) {
max = Math.max(max, citiesPerson[temp] + filter(paths, temp, citiesPerson));
}
return max;
}
/**
* 利用 dijkstra 算法,求出两点之间最近的距离,并且求出路径
*
* @param map 地图数据
* @param start 起点下标
*/
private static Vector<Vector<Integer>> dijkstra(int[][] map, int start) {
// 创建一个访问标记数组
boolean[] visited = new boolean[map.length];
// 创建一个路径数组
Vector<Vector<Integer>> paths = new Vector<>();
// 创建一个 start 到其他顶点的最短距离数组
int[] distance = new int[map.length];
// 初始化数据
{
// 源点到任意一个点所经过的点的上一个点的下标
// 标记源点访问过
visited[start] = true;
// 初始化一下源点到其他点的距离
for (int i = 0; i < map.length; i++) {
if (map[start][i] != 0XFFFF) {
Vector<Integer> vector = new Vector<>();
vector.add(start);
paths.add(vector);
} else {
paths.add(new Vector<>());
}
distance[i] = map[start][i];
}
}
// 由于已经初始化第一次的循环,所以从下一个开始即可,开始计算
for (int i = (start + 1) % map.length; i != start; i = (i + 1) % map.length) {
// 找到当前 distance 数组中最短的距离,并且不为 0
int minDistance = 0XFFFF, minIndex = -1;
for (int j = 0; j < distance.length; j++) {
// 找到没有访问国并且边权值最小的那条边
if (!visited[j] && distance[j] < minDistance) {
minDistance = distance[j];
minIndex = j;
}
}
// 标记 minIndex 访问过
visited[minIndex] = true;
// 修正最短距离
for (int j = 0; j < distance.length; j++) {
// 如果能多经过一个点,就多经过一个点,所以相等的情况也改变的它的路径,这就是这道题的一个难点,
// 因为从起点到目标顶点的最短距离可以有多个,经过的点越多,聚集的治疗团队就越多
if (!visited[j]) {
if (distance[minIndex] + map[minIndex][j] < distance[j]) {
distance[j] = distance[minIndex] + map[minIndex][j];
// 如果出现更优的方案,需要删除之前的最优方案,然后再添加最新的最优的方案
paths.get(j).clear();
paths.get(j).add(minIndex);
} else if (distance[minIndex] + map[minIndex][j] == distance[j]) {
// 如果出现相同的方案,追加新的方案
paths.get(j).add(minIndex);
}
}
}
}
return paths;
}
}
class Reader {
static BufferedReader reader;
private static StringTokenizer tokenizer;
/** call this method to initialize reader for InputStream */
public static void init(InputStream input) {
reader = new BufferedReader(new InputStreamReader(input));
tokenizer = new StringTokenizer("");
}
public static String nextLine() throws IOException {
return reader.readLine();
}
/** get next word */
public static String next() throws IOException {
while (!tokenizer.hasMoreTokens()) {
tokenizer = new StringTokenizer(reader.readLine());
}
return tokenizer.nextToken();
}
public static int nextInt() throws IOException {
return Integer.parseInt(next());
}
public static double nextDouble() throws IOException {
return Double.parseDouble(next());
}
}