[LeetCode 1368] Minimum Cost to Make at Least One Valid Path in a Grid

Given a m x n grid. Each cell of the grid has a sign pointing to the next cell you should visit if you are currently in this cell. The sign of grid[i][j] can be:

  • 1 which means go to the cell to the right. (i.e go from grid[i][j] to grid[i][j + 1])
  • 2 which means go to the cell to the left. (i.e go from grid[i][j] to grid[i][j - 1])
  • 3 which means go to the lower cell. (i.e go from grid[i][j] to grid[i + 1][j])
  • 4 which means go to the upper cell. (i.e go from grid[i][j] to grid[i - 1][j])

Notice that there could be some invalid signs on the cells of the grid which points outside the grid.

You will initially start at the upper left cell (0,0). A valid path in the grid is a path which starts from the upper left cell (0,0) and ends at the bottom-right cell (m - 1, n - 1) following the signs on the grid. The valid path doesn‘t have to be the shortest.

You can modify the sign on a cell with cost = 1. You can modify the sign on a cell one time only.

Return the minimum cost to make the grid have at least one valid path.

Example 1:

Input: grid = [[1,1,1,1],[2,2,2,2],[1,1,1,1],[2,2,2,2]]
Output: 3
Explanation: You will start at point (0, 0).
The path to (3, 3) is as follows. (0, 0) --> (0, 1) --> (0, 2) --> (0, 3) change the arrow to down with cost = 1 --> (1, 3) --> (1, 2) --> (1, 1) --> (1, 0) change the arrow to down with cost = 1 --> (2, 0) --> (2, 1) --> (2, 2) --> (2, 3) change the arrow to down with cost = 1 --> (3, 3)
The total cost = 3.

Example 2:

Input: grid = [[1,1,3],[3,2,2],[1,1,4]]
Output: 0
Explanation: You can follow the path from (0, 0) to (2, 2).

Example 3:

Input: grid = [[1,2],[4,3]]
Output: 1

Example 4:

Input: grid = [[2,2,2],[2,2,2]]
Output: 3

Example 5:

Input: grid = [[4]]
Output: 0

Constraints:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 100

Bottom up DP does not work as a cell can visit all its 4 neighbors. Initially I came up with a standard BFS then update each cell‘s min cost during traversal. Each cell is only visited once.  The problem using a Queue in BFS is that the traversal order is decided by the insertion order of a cell and has nothing to do with the cost to reach a cell. This is incorrect, a counter example in example 1 is that we‘ll visit cell(1, 0) with a cost of 1 when instead there is a 0 cost path to reach (1, 0).

Solution 1. PriorityQueue

To fix the above incorrect traversal order, we use PriorityQueue and track each cell‘s cost to reach from (0, 0). If the current visiting cell has a bigger cost than what have been reported so far as min cost, skip this cell as we know there is a better path with smaller cost to reach this cell. (We must not skip equal cost cells as we don‘t know which one will lead to an optimal answer so we need to check all of them)

The runtime is similar with Dijkstra‘s runtime, O(M * N + M * N * log (M * N))

class Solution {
    public int minCost(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int[][] cost = new int[m][n];
        for(int i = 0; i < m; i++) {
            Arrays.fill(cost[i], (int)1e7);
        }
        PriorityQueue<int[]> q = new PriorityQueue<>(Comparator.comparingInt(a -> a[2]));
        q.add(new int[]{0,0,0});
        cost[0][0] = 0;

        int[] dx = {0,0,1,-1};
        int[] dy = {1,-1,0,0};

        while(q.size() > 0) {
            int[] curr = q.poll();
            if(curr[2] > cost[curr[0]][curr[1]]) {
                continue;
            }
            if(curr[0] == m - 1 && curr[1] == n - 1) {
                break;
            }
            int k = grid[curr[0]][curr[1]];
            for(int i = 0; i < 4; i++) {
                int x = curr[0] + dx[i];
                int y = curr[1] + dy[i];
                int c = cost[curr[0]][curr[1]] + (i == k - 1 ? 0 : 1);
                if(x >= 0 && x < m && y >= 0 && y < n && c < cost[x][y]) {
                    cost[x][y] = c;
                    q.add(new int[]{x,y,c});
                }
            }
        }
        return cost[m - 1][n - 1];
    }
}

Solution 2. Deque

The reason that we use PriorityQueue in solution 1 is to ensure the correct traversal order based on cost from starting point. We can also use a Deque and do the following to achieve the same.

1. If visiting its neighbor‘s cost is 0, i.e, the current cell already has the direction to visit this neighbor, we add this neighbor along with cost to the front of deque.

2. Otherwise, we add this neighbor along with cost (+1  in this case) to the back of deque.

Polling from the front of deque ensures that we keep visiting cells with no added costs until otherwise. Once a cell is visited, we mark it and won‘t visit it again.

The runtime is O(M * N).

class Solution {
    public int minCost(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        boolean[][] visited = new boolean[m][n];
        Deque<int[]> dq = new ArrayDeque<>();
        dq.add(new int[]{0,0,0});

        int[] dx = {0,0,1,-1};
        int[] dy = {1,-1,0,0};

        while(dq.size() > 0) {
            int[] curr = dq.pollFirst();
            if(visited[curr[0]][curr[1]]) {
                continue;
            }
            visited[curr[0]][curr[1]] = true;
            if(curr[0] == m - 1 && curr[1] == n - 1) {
                return curr[2];
            }
            for(int i = 0; i < 4; i++) {
                int x = curr[0] + dx[i];
                int y = curr[1] + dy[i];
                if(x >= 0 && x < m && y >= 0 && y < n && !visited[x][y]) {
                    if(i + 1 == grid[curr[0]][curr[1]]) {
                        dq.addFirst(new int[]{x, y, curr[2]});
                    }
                    else {
                        dq.addLast(new int[]{x, y, curr[2] + 1});
                    }
                }
            }
        }
        return -1;
    }
}

Solution 3. DFS + BFS

The gist of solution 2 is essentially using a deque to ensure we exhaust all reachable cells without adding any cost, then try to change cell directions with added cost to explore unvisited cells. This can also be done by the following DFS then BFS algorithm.

1. From the start point, do a dfs to visit all reachable cells with cost 0, update these cells‘ cost and add them into a Queue.

2. As long as the queue is not empty, repeat the following.

2a. increment the current cost by 1, representing we‘ve exhausted all reachable cells and need to allow 1 more direction change to keep exploring.

2b. for all the cell in queue, do no-added-cost dfs on their neighbors if neighbors haven‘t been visited. Update these cells‘ cost and add all reached cells to queue. These newly added cells will be the starting points from the next bfs iteration.

The above algorithm is a controlled version of the standard BFS. At each BFS iteration, we update the current allowed cost and use DFS to add all unvisited cells that have 0 cost to reach from the cells in the current BFS iteration.

The runtime is O(M * N).

class Solution {
    private int[] dx = {0,0,1,-1};
    private int[] dy = {1,-1,0,0};
    private int m;
    private int n;
    private int[][] cost;
    private Queue<int[]> q;
    public int minCost(int[][] grid) {
        int currCost = 0;
        m = grid.length;
        n = grid[0].length;
        cost = new int[m][n];
        for(int i = 0; i < m; i++) {
            Arrays.fill(cost[i], Integer.MAX_VALUE);
        }
        q = new LinkedList<>();
        dfs(grid, 0, 0, currCost);

        while(!q.isEmpty()) {
            currCost++;
            int sz = q.size();
            for(int i = 0; i < sz; i++) {
                int[] curr = q.poll();
                for(int j = 0; j < 4; j++) {
                    dfs(grid, curr[0] + dx[j], curr[1] + dy[j], currCost);
                }
            }
        }
        return cost[m - 1][n - 1];
    }
    private void dfs(int[][] grid, int x, int y, int currCost) {
        if(x < 0 || x >= m || y < 0 || y >= n || cost[x][y] < Integer.MAX_VALUE) {
            return;
        }
        cost[x][y] = currCost;
        q.add(new int[]{x, y});
        dfs(grid, x + dx[grid[x][y] - 1], y + dy[grid[x][y] - 1], currCost);
    }
}

原文地址:https://www.cnblogs.com/lz87/p/12393706.html

时间: 2024-11-08 08:00:00

[LeetCode 1368] Minimum Cost to Make at Least One Valid Path in a Grid的相关文章

[LeetCode] 1000. Minimum Cost to Merge Stones

There are N piles of stones arranged in a row.  The i-th pile has stones[i] stones. A move consists of merging exactly K consecutive piles into one pile, and the cost of this move is equal to the total number of stones in these K piles. Find the mini

[LeetCode] 857. Minimum Cost to Hire K Workers 雇佣K名工人的最低成本

There are?N?workers.? The?i-th worker has a?quality[i]?and a minimum wage expectation?wage[i]. Now we want to hire exactly?K?workers to form a?paid group.? When hiring a group of K workers, we must pay them according to the following rules: Every wor

LeetCode 857. Minimum Cost to Hire K Workers

合法的pay group里所有worker的比例是一样的,即 wage1/wage2 = quality1/quality2 推出 wage1/quality1 = wage2/quality2. 这就好办了,定义 ratio_i = wage_i/quality_i.对于一个group,ratio一定是所有人中最大的那个. 对于一个大小为k的group,需要pay的钱就是 Σ_k quality_i * 最大的ratio. 为了避免每次扫一遍group找到最大的ratio,我们根据ratio排

LeetCode --- 64. Minimum Path Sum

题目链接:Minimum Path Sum Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path. Note: You can only move either down or right at any point in time. 这道题的要求是在m*n

【Leetcode】Minimum Path Sum

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path. Note: You can only move either down or right at any point in time. 思路:简单的动态规划题目,设f(m, n)为从(0, 0)到达(m

【leetcode】Minimum Window Substring

问题: 给定两个字符串,S,T,返回S中包含T中所有字符的最短的字串,若不存在,则返回"".时间复杂度为O(n). 例如:S = "ADOBCODEBANC" T = "ABC" 返回BANC 生活场景: 把问题具体化现实化一点.有n层楼,每层楼里放有一个物品,现在老板给你一个物品清单,里面是要你集齐的物品,你可以乘坐电梯,但是电梯只停一次,停在哪一层,就从哪一层开始向楼上搜集物品,至于要在那层停电梯,由你自己选择. 这里我们当然选择即能集齐物品

POJ 2516 Minimum Cost(最小费用最大流啊)

题目链接:http://poj.org/problem?id=2516 Description Dearboy, a goods victualer, now comes to a big problem, and he needs your help. In his sale area there are N shopkeepers (marked from 1 to N) which stocks goods from him.Dearboy has M supply places (mar

POJ 2516 Minimum Cost (最小费用最大流)

POJ 2516 Minimum Cost 链接:http://poj.org/problem?id=2516 题意:有M个仓库,N个商人,K种物品.先输入N,M,K.然后输入N行K个数,每一行代表一个商人要购买的物品,其中K个数分别表示要购买的每件商品数.然后是M行K个数,每行表示仓库里的情况,其中K个数分别每种物品的库存量.接下来是K个矩阵,每个矩阵为N*M,分别表示第K种物品从M个仓库运到第N个商人的花费.问能否合理安排,使得花费最少,如果不行就输出-1. 思路: 一开始的时候,竟然构造了

POJ 2516 Minimum Cost

Minimum Cost Time Limit: 4000ms Memory Limit: 65536KB This problem will be judged on PKU. Original ID: 251664-bit integer IO format: %lld      Java class name: Main Dearboy, a goods victualer, now comes to a big problem, and he needs your help. In hi