在刷题的过程中常常会遇到求最短路径的问题,在求无权图的问题中我们常常使用BFS来求其最短路径,而BFS无法解决网(有权图)中的问题,我们解决网中的最短路径常常使用dijkstra算法来求解。
dijkstra算法是一种贪心的思想,具体其正确性的证明,这里就不再赘述。下面来直接讲解如何使用dijkstra算法:
1.我们在使用dijkstra算法时为了其编写的便捷性一般使用邻接矩阵来描述一个图。并将邻接矩阵中每个元素的值初始化为无穷大(或者很大很大的值)。之后根据图中的结构加入数字,称这个矩阵为E[][]。如下:
2.接下来我们还需要3个矩阵,分别为dist[] , collected[] , path[] 。dist用来存放遍历节点的距离信息,初始化为所求节点到其他各点的距离信息;collected[]用来存放是否dist中收录过该节点,初始化为false ;path[]用来存放路径,初始化为-1 。
3. dijkstra算法的具体过程的伪代码如下:
public void dijkstra(boolean collected[],int[] dist,int[][] E,int[] path)
{
while(1)
{
v = collected[]为false的点中dist[]值最小的顶点;
if(v不存在) break ;
collected[v] = true ;
for(v顶点的每个邻接点w)
if(collected[w] == false)
{
if(dist[v] + E[v][w] < dist[w])
{
dist[w] = dist[v] + E[v][w] ;
path[w] = v ;
}
}
}
}
以上为dijkstra算法的基本过程,下面选一道dijkstra算法的题目来具体解释一下:
PAT1003
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
(大家自己翻译吧……)
这道题目很明显是要求我们求出有多少种距离相同的最短路径,同时他还要求一个最多的人员数
完整代码如下:
import java.util.*; public class Main { public static void main(String args[]) { Scanner scanner = new Scanner(System.in); int cities = scanner.nextInt(); int roads = scanner.nextInt(); int start = scanner.nextInt(); int end = scanner.nextInt(); int[] dis = new int[cities]; int[][] map = new int[roads][roads]; boolean[] set = new boolean[cities]; int[] weigh = new int[cities] ; int[] w = new int[cities]; int[] num = new int[cities] ; for(int i = 0 ; i < cities ; i ++) { Arrays.fill(map[i], 9999999); } Arrays.fill(dis, 9999999); for(int i = 0 ; i < cities ; i ++) { weigh[i] = scanner.nextInt(); } for(int i = 0 ; i < roads ; i ++) { int x = scanner.nextInt(); int y = scanner.nextInt(); int z = scanner.nextInt(); map[x][y] = map[y][x] = z; } scanner.close(); dis[start] = 0 ; w[start] = weigh[start] ; num[start] = 1 ; for(int i = 0 ; i < cities ; i ++) { int index = -1 ; int min = 9999999; for(int j = 0 ; j < cities ; j ++) { if(set[j] == false && dis[j] < min) { min = dis[j]; index = j ; } } if(index == -1) break ; set[index] = true ; for(int j = 0 ; j < cities ; j ++) { if(set[j] == false && map[index][j] != 9999999) { if(min + map[index][j] < dis[j]) { dis[j] = min + map[index][j] ; num[j] = num[index] ; w[j] = w[index] + weigh[j] ; } else if(min + map[index][j] == dis[j]) { num[j] = num[j] + num[index] ; if(w[index] + weigh[j] > w[j]) { w[j] = w[index] + weigh[j] ; } } } } } System.out.print(num[end] + " " + w[end]); } }
下面我们来逐步分析:
1.读取数据以及构建邻接矩阵:
Scanner scanner = new Scanner(System.in);
int cities = scanner.nextInt();
int roads = scanner.nextInt();
int start = scanner.nextInt();
int end = scanner.nextInt();
int[] dis = new int[cities]; //构建dist[]
int[][] map = new int[roads][roads]; //邻接矩阵E
boolean[] set = new boolean[cities]; //collected[]
int[] weigh = new int[cities] ; //记录每个城市救援队的数目
int[] w = new int[cities]; //到达每个城市的救援队的最多数量
int[] num = new int[cities] ; //相同距离有多少种走法
for(int i = 0 ; i < cities ; i ++) //初始化邻接矩阵
{
Arrays.fill(map[i], 9999999);
}
Arrays.fill(dis, 9999999);
for(int i = 0 ; i < cities ; i ++) //初始化每个城市的救援队
{
weigh[i] = scanner.nextInt();
}
for(int i = 0 ; i < roads ; i ++) //记录邻接矩阵
{
int x = scanner.nextInt();
int y = scanner.nextInt();
int z = scanner.nextInt();
map[x][y] = map[y][x] = z;
}
scanner.close();
2.使用dijkstra算法求最短路径:
dis[start] = 0 ; //起点到七点的距离为0
w[start] = weigh[start] ;
num[start] = 1 ; //初始化起点的走法有一种
for(int i = 0 ; i < cities ; i ++)
{
int index = -1 ;
int min = 9999999;
for(int j = 0 ; j < cities ; j ++)
{
if(set[j] == false && dis[j] < min) //寻找dist[]中没有被收录的最小元素
{
min = dis[j];
index = j ;
}
}
if(index == -1) break ; //如果没有最小元素,则说明最短路径已经寻找完毕
set[index] = true ; //collected[]设置为true 表示已经收录该节点
for(int j = 0 ; j < cities ; j ++) //查找v相邻元素的距离
{
if(set[j] == false && map[index][j] != 9999999)
{
if(min + map[index][j] < dis[j])
{
dis[j] = min + map[index][j] ; //更新最短距离
num[j] = num[index] ; //更新走法
w[j] = w[index] + weigh[j] ; //记录最短距离的救援队的数目
}
else if(min + map[index][j] == dis[j]) //当最短距离相同时
{
num[j] = num[j] + num[index] ; //距离相同时,更新走法
if(w[index] + weigh[j] > w[j])
{
w[j] = w[index] + weigh[j] ; //距离相同时救援队记录最多的那个 }
}
}
}
}