2023-11-23
难度 中等
1334. 阈值距离内邻居最少的城市 - 力扣(LeetCode)
有
n
个城市,按从0
到n-1
编号。给你一个边数组edges
,其中edges[i] = [fromi, toi, weighti]
代表fromi
和toi
两个城市之间的双向加权边,距离阈值是一个整数distanceThreshold
。返回能通过某些路径到达其他城市数目最少、且路径距离 最大 为
distanceThreshold
的城市。如果有多个这样的城市,则返回编号最大的城市。注意,连接城市 i 和 j 的路径的距离等于沿该路径的所有边的权重之和。
示例 1:
输入:n = 4, edges = [[0,1,3],[1,2,1],[1,3,4],[2,3,1]], distanceThreshold = 4 输出:3 解释:城市分布图如上。 每个城市阈值距离 distanceThreshold = 4 内的邻居城市分别是: 城市 0 -> [城市 1, 城市 2] 城市 1 -> [城市 0, 城市 2, 城市 3] 城市 2 -> [城市 0, 城市 1, 城市 3] 城市 3 -> [城市 1, 城市 2] 城市 0 和 3 在阈值距离 4 以内都有 2 个邻居城市,但是我们必须返回城市 3,因为它的编号最大。示例 2:
输入:n = 5, edges = [[0,1,2],[0,4,8],[1,2,3],[1,4,2],[2,3,1],[3,4,1]], distanceThreshold = 2 输出:0 解释:城市分布图如上。 每个城市阈值距离 distanceThreshold = 2 内的邻居城市分别是: 城市 0 -> [城市 1] 城市 1 -> [城市 0, 城市 4] 城市 2 -> [城市 3, 城市 4] 城市 3 -> [城市 2, 城市 4] 城市 4 -> [城市 1, 城市 2, 城市 3] 城市 0 在阈值距离 2 以内只有 1 个邻居城市。
使用弗洛伊德算法邻接矩阵
首先讲一下弗洛伊德算法和邻接矩阵
邻接矩阵是一个对称矩阵,长和宽都和图的顶点数目相同,该处的数字为两个顶点之间的距离,对于不可到达的顶点距离一般设置为最大值,无向图是对称矩阵
0 | 3 | INF | INF |
3 | 0 | 1 | 4 |
INF | 1 | 0 | 1 |
INF | 4 | 1 | 0 |
从这个矩阵中可以清晰地看到目标点之间的距离,这里我们可以尝试获取顶点0和顶点1的一个距离,对于直接连接的顶点直接取dist [ 0 ][ 1 ],而对于间接连接的顶点,比如顶点0和顶点3,距离dist[ 0 ][ 3 ]为INF,但并非不可到达,可以连续经过其他点到达顶点3,我们可以从顶点0先到达第一站,任意一个和顶点0直接连接的顶点这里只有1 dist[ 1 ],所以我们前往顶点1,此时就变成了从顶点1到达顶点3 dist[ 1 ][ 3 ],而顶点1可以直接到达顶点3。
或者我们到达顶点1后来到第二站,第二站一般不取走过的站,这里可以选择顶点2 dist[ 1 ][ 2 ],然后再从顶点2到达顶点3 dist[ 2 ][ 3 ]。
对于上述两条路线,除了经过的站点的数量,移动的距离也受到了影响,第一条路线的长度为 dist[ 0 ][ 1 ]+dist[ 1 ][ 3 ]=1+4=5
另一条为dist[ 0 ][ 1 ]+dist[ 1 ][ 2 ]+dist[ 2 ][ 3 ] = 1+1+1=3
显然我们可以根据需求寻找我们所需要的走法,这题和弗洛伊德算法一样求的是最短路径
弗洛伊德算法状态转移方程为
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
i,j,k这个三个参数可能不好理解,至少我不怎么理解,那么来模拟一下寻找最短路径
回到之前的图和邻接矩阵,其中任何dist[i][i]都是0,任何dist[i][j]都等于dist[j][i]
0 | 3 | INF | INF |
3 | 0 | 1 | 4 |
INF | 1 | 0 | 1 |
INF | 4 | 1 | 0 |
比如我们打算获取顶点1到顶点3的最小距离,需要不断从各种路线取最小值,最后的dist[1][3]即是1和3距离的最小值
第一步:dist[1][3] = min(dist[1][3],dist[1][0]+dist[0][3]),即是dist[i][j] = min(dist[i][j],dist[i][k]+dist[k][j]),min中的两个参数一个是等号左边的数字,用于迭代最小值,第二个参数就是取两个顶点都连接或者间接连接的顶点的距离之和
回到第一步,dist[1][3] = min(dist[1][3],dist[1][0]+dist[0][3]),因为dist[0][3]是INF,所以跳过,最小值还是初始的4
第二步:dist[1][3] = min(dist[1][3],dist[1][1]+dist[1][3]),啥也没发生
第三步:dist[1][3] = min(dist[1][3],dist[1][2]+dist[2][3]),可以算出来后者更小
第四步:dist[1][3] = min(dist[1][3],dist[1][3]+dist[3][3]),啥也没发生
最后得到的最小的dist[1][3]就是dist[1][2]+dist[2][3],也就是2
所以这三个参数就很简单了,i和j表示dist[i][j]的值,k就是中间点的位置,虽然是个n*n矩阵但是只有n个值,这里的k就是表示第k个值
看似已经没问题了,但是不确定dist[2][3]的值是否是最小,dist[2][3]是否存在类似dist[2][1]+dist[1][3]这种值,虽然这里更大,但是不排除其他情况有更小的值,所以还是要从dist[0][0]到dist[3][3]取最小值,也就是动态规划
public int findTheCity(int n, int[][] edges, int distanceThreshold) {
int[][] matrix = new int[n][n];//创建矩阵
for (int i = 0; i < n; i++) {//初始化矩阵
Arrays.fill(matrix[i],Integer.MAX_VALUE);
//对于不可到达的矩阵取最大值用于校验,走到了就是死路,只能换路
//因为这个矩阵是整型矩阵所以就不要填写别的类型了
matrix[i][i] = 0;//自己到自己的距离就是0
}
for (int[] edge:edges){
matrix[edge[0]][edge[1]] = edge[2];//不断将edges中的数据填到矩阵中
matrix[edge[1]][edge[0]] = edge[2];//对称换一下0和1的位置就够了
}
for (int i = 0; i < n; i++)//第i行的值
for (int j = 0; j < n; j++)//第j列的值
for (int k = 0; k < n; k++)//然后从第一个顶点开始加
if (matrix[j][i]!=Integer.MAX_VALUE&&
matrix[i][k]!=Integer.MAX_VALUE)//不能走到死路
matrix[j][k] = Math.min(matrix[i][j],matrix[j][i]+matrix[i][k]);
//没走到死路即对比值,这个循环会使用所有的顶点作为中继点,然后会逐个替换这个值
//循环结束之后整个矩阵已经成了一个新的矩阵,矩阵的每个点的值都是这个点的两个顶点的最小值
int min = Integer.MAX_VALUE;
int res = -1;
for (int i = 0; i < n; i++) {//遍历矩阵,同时找最小值
int count = 0;
for (int j = 0; j < n; j++) {
if (matrix[i][j]<=distanceThreshold)
count++;//找到从顶点i到达其他顶点的距离小于等于目标距离的路线数量
}
if (count<=min){
min = count;
res = i;//迭代最小值
}
}
System.out.println("the world!");
return res;
}