<LeetCode>1631. 最小体力消耗路径

题目描述

题目链接
在这里插入图片描述
在这里插入图片描述

解题思路

开始想着,每个节点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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值