题目描述
解题思路
开始想着,每个节点4种选择,用搜索算法试试。但是复杂度指数级别,又不能设计好的启发式剪枝,放弃。
动态规划?
看起来每个状态只与相邻4个状态有关,但是需要考虑全局路径,也不行。
于是想着把给的数组转换为一个图。行列<=100,最多10000个节点,应该是可行的。每个节点最多出去4条边,边的权值为二维数组相邻格子之差的绝对值。
得到一个有向图之后,题面转换为找起点到终点的最优路径,路径花费值是最大边。这里有多种解决方式。
-
Dijsktra最短路算法
需要重新定义边的权值,这个方法有点巧妙,反正Dijsktra算法本质就是启发式算法,A*搜索启发式f=g+h,g表示到起点实际花费,h表示到终点预估花费。人工智能领域中h=0时又叫一致代价搜索!只要设计一种满足一致性的启发式即可! -
并查集
最大边,不由得想到最小生成树算法Kruskal算法的最后一条边。
但是此处只需要找一条路径即可,因此在Kruskal算法基础上改进以下,起点和终点联通时算法结束,即可得到解。···
Java实现
class Solution {
static int[] parent = new int[10005];
static class Edge{
int from;
int to;
int weight;
public Edge(int from, int to, int weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
}
public int minimumEffortPath(int[][] heights) {
int MAX_SIZE = 10005;
int lx = heights.length;
int ly = heights[0].length;
List<Edge> array = new ArrayList<Edge>();
for(int i = 0;i < lx;i++){
for(int j = 0;j < ly;j++){
//上
if(i>0){
int w = Math.abs(heights[i][j]-heights[i-1][j]);
array.add(new Edge(i*ly+j,(i-1)*ly+j,w));
}
//下
if(i<lx-1){
int w = Math.abs(heights[i][j]-heights[i+1][j]);
array.add(new Edge(i*ly+j,(i+1)*ly+j,w));
}
//左
if(j>0){
int w = Math.abs(heights[i][j]-heights[i][j-1]);
array.add(new Edge(i*ly+j,i*ly+j-1,w));
}
//右
if(j<ly-1){
int w = Math.abs(heights[i][j]-heights[i][j+1]);
array.add(new Edge(i*ly+j,i*ly+j+1,w));
}
}
}
Collections.sort(array, new Comparator<Edge>() {
@Override
public int compare(Edge o1, Edge o2) {
return o1.weight<o2.weight ? -1: (o1.weight==o2.weight?0:1);
}
});
for(int i = 0;i < lx*ly;i++){
parent[i] = i;
}
int ans = 0;
for(int i = 0;i < array.size();i++){
Edge e = array.get(i);
if(Find(e.from)!=Find(e.to)){
Union(e.from,e.to);
}
if(Find(0)==Find(lx*ly-1)){
ans = e.weight;
break;
}
}
return ans;
}
static void Union(int x,int y){
if(Find(x)!=Find(y)){
parent[Find(x)] = Find(y);
}
}
static int Find(int x){
return x == parent[x] ? x : (parent[x] = Find(parent[x]));
}
}
正好复习了以下并查集,以及一些图算法,并查集的Find方法中使用了一行代码同时实现了路径压缩!!!
复杂度分析
时间复杂度主要在排序
m,n为二维数组的行和列。
O(mn*logmn)