题目:
光明小学的小朋友们要举行一年一度的接力跑大赛了,但是小朋友们却遇到了一个难题:设计接力跑大赛的线路,你能帮助他们完成这项工作么?
光明小学可以抽象成一张有N个节点的图,每两点间都有一条道路相连。光明小学的每个班都有M个学生,所以你要为他们设计出一条恰好经过M条边的路径。
光明小学的小朋友们希望全盘考虑所有的因素,所以你需要把任意两点间经过M条边的最短路径的距离输出出来以供参考。
你需要设计这样一个函数:
res[][] Solve( N, M, map[][]);
注意:map必然是N * N的二维数组,且map[i][j] == map[j][i],map[i][i] == 0,-1e8 <= map[i][j] <= 1e8。(道路全部是无向边,无自环)2 <= N <= 100, 2 <= M <= 1e6。
map数组表示了一张稠密图,其中任意两个不同节点i,j间都有一条边,边的长度为map[i][j]。N表示其中的节点数。
你要返回的数组也必然是一个N * N的二维数组,表示从i出发走到j,经过M条边的最短路径
你的路径中应考虑包含重复边的情况。
样例:
N = 3
M = 2
map = {
{0, 2, 3},
{2, 0, 1},
{3, 1, 0}
}
输出结果result为:
result = {
{4, 4, 3},
{4, 2, 5},
{3, 5, 2}
}
输入样例:
3
2
3 3
0 2 3
2 0 1
3 1 0
输出样例:
[[4, 4, 3],
[4, 2, 5],
[3, 5, 2]]
解题思路:
首先第一点是肯定的,全排列肯定是可以求出解的,但是时间复杂度那肯定是爆炸的,当然,30分钟的时间里面能把全排列写出来也是可以的了我感觉。
但是还是想试试动态规划能不能求解,我们把每一步的中间结果都存下来,相当于对搜索进行了剪枝操作。当然,动态规划是基于以下的假设:
如果从点s到点t存在最小路径而且正好经过点c,那从s到c的所有路径当中,这条s->c一定是最短路径。
很好理解,如果当前这条s->c(我们称之为l1)不是最短的话,那肯定存在一条s->c路径(l2)最短,那走2到t肯定比走l1到t短,所以l1肯定就被替换掉了。
代码实现:
全排列版:
public class Test {
private static int[][] resultMap;
private static int times = 0;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 初始化
int N = scanner.nextInt();
int M = scanner.nextInt();
int[][] map = new int[N][N];
for (int i = 0; i < N; i ++) {
for (int j = 0; j < N; j ++) {
map[i][j] = scanner.nextInt();
}
}
resultMap = new int[N][N];
// 执行
for (int i = 0; i < N; i ++) {
route(map, N, M, 1, i, i, 0);
}
// 打印
System.out.println("total recursion: " + times);
for (int i = 0; i < N; i ++) {
for (int j = 0; j < N; j ++) {
System.out.print(resultMap[i][j] + " ");
}
System.out.println("");
}
}
private static void route(int[][] map, int N, int M, int step, int head, int now, int cost) {
times ++;
if (step > M) {
resultMap[head][now] = resultMap[head][now] == 0 ? cost : (Math.min(resultMap[head][now], cost));
return;
}
for (int i = 0; i < N; i ++) {
if (i == now) continue;
route(map, N, M, step + 1, head, i, cost + map[now][i]);
}
}
}
动态规划版:
public class Test {
private static int[][][] resultMap;
private static int times = 0;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 初始化
int N = scanner.nextInt();
int M = scanner.nextInt();
int[][] map = new int[N][N];
for (int i = 0; i < N; i ++) {
for (int j = 0; j < N; j ++) {
map[i][j] = scanner.nextInt();
}
}
resultMap = new int[M][N][N];
// 执行,动态规划
// 初始化步数为1的情况
for (int i = 0; i < N; i ++) {
for (int j = 0; j < N; j ++) {
resultMap[0][i][j] = map[i][j];
}
}
// 更新步数2-M的情况
for (int k = 1; k < M; k ++) {
for (int i = 0; i < N; i ++) {
for (int j = 0; j < N; j ++) {
for (int p = 0; p < N; p ++) {
times ++;
if (p == j || resultMap[k - 1][i][p] == 0) continue;
resultMap[k][i][j] = Math.min(resultMap[k][i][j] == 0 ? Integer.MAX_VALUE : resultMap[k][i][j], resultMap[k - 1][i][p] + map[p][j]);
}
}
}
}
// 打印
System.out.println("total recursion: " + times);
for (int i = 0; i < N; i ++) {
for (int j = 0; j < N; j ++) {
System.out.print(resultMap[resultMap.length - 1][i][j] + " ");
}
System.out.println("");
}
}
}
测试结果
3
2
0 2 3
2 0 1
3 1 0
total recursion: 21
4 4 3
4 2 5
3 5 2