Leetcode 1663 最小体力消耗 并查集 PHP

386 阅读2分钟

并查集还能这么玩。

题目

最近做了一道力扣题,leetcode-cn.com/problems/pa…

思路

leetcode-cn.com/problems/pa… 参考大佬的思路。

  • 把格子中的每个顶点抽象成图中的一个顶点。
  • 把两个相邻的格子之间连一条边,边的长度是两个格子数值差的绝对值
  • 找一条从左上角到右下角的最短路径,其中路径的长度定义为路径上所有边的权值的最大值

方法一、二分

对最短路径长度进行二分。当我们二分枚举到长度x的时候,我们只保留所有长度小于等于x的边。然后从左上角开始进行搜索,BFS和DFS均可,只需要判断最终有没有路径到达右下角即可。如果能够到达,那么我们可以减小x,反之则增大。php代码如下:

class Solution {

    /**
     * @param Integer[][] $heights
     * @return Integer
     */
    function minimumEffortPath($heights) {
        $row = count($heights);
        $col = count($heights[0]);
        $left = 0;
        $right = 999999;
        $res = 0;
        $directions = [[0, 1], [0, -1], [1, 0], [-1, 0]];
        while ($left < $right) {
            $mid = $left + (int)(($right - $left) / 2);
            $visited = array_fill(0, $row, array_fill(0, $col, 0));
            $queue = new SplQueue();
            $queue->enqueue([0, 0]);
            $visited[0][0] = 1;
            while (!$queue->isEmpty()) {
                $top = $queue->dequeue();
                $x = $top[0];
                $y = $top[1];
                foreach ($directions as $direction) {
                    $newX = $x + $direction[0];
                    $newY = $y + $direction[1];
                    if ($newX >= 0 && $newX < $row &&
                        $newY >= 0 && $newY < $col &&
                    $visited[$newX][$newY] == 0 &&
                        abs($heights[$newX][$newY] - $heights[$x][$y]) <= $mid
                    ) {
                        $queue->enqueue([$newX, $newY]);
                        $visited[$newX][$newY] = 1;
                    }
                }
            }
            if ($visited[$row - 1][$col - 1] == 1) {
                $right = $mid;
//                $res = $mid;
            } else {
                $left = $mid + 1;
            }
        }

        return $left;
    }

}

方法二、并查集

没想到这玩意还能用并查集做。

把所有的边按照长度大小从小到大依次添加到并查集,直到左上角和右下角联通即可。

class Solution {

    /**
     * @param Integer[][] $heights
     * @return Integer
     */
    function minimumEffortPath($heights)
    {
        $row = count($heights);
        $col = count($heights[0]);
        $edges = [];
        for ($i = 0; $i < $row; $i++) {
            for ($j = 0; $j < $col; $j++) {
                $id = $i * $col + $j;
                if ($i > 0) {
                    $edges[] = [$id - $col, $id, abs($heights[$i][$j] - $heights[$i-1][$j])];
                }
                if ($j > 0) {
                    $edges[] = [$id - 1, $id, abs($heights[$i][$j] - $heights[$i][$j-1])];
                }
            }
        }
        //按照边的长短升序
        usort($edges, function ($a, $b) {
            if ($a[2] > $b[2]) {
                return 1;
            } else {
                return -1;
            }
        });

        $uf = new UnionFind($row * $col);
        foreach ($edges as $edge) {
            $uf->union($edge[0], $edge[1]);
            if ($uf->connection(0, $row * $col - 1)) {
                return $edge[2];
            }
        }

        return 0;
    }


    function minimumEffortPathAC($heights) {
        $row = count($heights);
        $col = count($heights[0]);
        $left = 0;
        $right = 999999;
        $directions = [[0, 1], [0, -1], [1, 0], [-1, 0]];
        while ($left < $right) {
            $mid = $left + (int)(($right - $left) / 2);
            $visited = array_fill(0, $row, array_fill(0, $col, 0));
            $queue = new SplQueue();
            $queue->enqueue([0, 0]);
            $visited[0][0] = 1;
            while (!$queue->isEmpty()) {
                $top = $queue->dequeue();
                $x = $top[0];
                $y = $top[1];
                foreach ($directions as $direction) {
                    $newX = $x + $direction[0];
                    $newY = $y + $direction[1];
                    if ($newX >= 0 && $newX < $row &&
                        $newY >= 0 && $newY < $col &&
                    $visited[$newX][$newY] == 0 &&
                        abs($heights[$newX][$newY] - $heights[$x][$y]) <= $mid
                    ) {
                        $queue->enqueue([$newX, $newY]);
                        $visited[$newX][$newY] = 1;
                    }
                }
            }
            if ($visited[$row - 1][$col - 1] == 1) {
                $right = $mid;
//                $res = $mid;
            } else {
                $left = $mid + 1;
            }
        }

        return $left;
    }

}

class UnionFind
{
    public $parents;
    public $count;
    public function __construct($n)
    {
        for ($i = 0; $i < $n; $i++) {
            $this->parents[$i] = $i;
            $this->count++;
        }
    }
    public function find($x)
    {
        while ($x != $this->parents[$x]) {
            $this->parents[$x] = $this->parents[$this->parents[$x]];
            $x = $this->parents[$x];
        }
        return $x;
    }
    public function union($x, $y)
    {
        $rootX = $this->find($x);
        $rootY = $this->find($y);
        if ($rootY == $rootX) {
            return;
        }
        $this->parents[$rootX] = $rootY;
        $this->count--;
        return;
    }
    public function connection($x, $y)
    {
        $rootX = $this->find($x);
        $rootY = $this->find($y);
        if ($rootY == $rootX) {
            return true;
        }
        return false;
    }
}