zoukankan      html  css  js  c++  java
  • [LeetCode 1284] Minimum Number of Flips to Convert Binary Matrix to Zero Matrix

    Given a m x n binary matrix mat. In one step, you can choose one cell and flip it and all the four neighbours of it if they exist (Flip is changing 1 to 0 and 0 to 1). A pair of cells are called neighboors if they share one edge.

    Return the minimum number of steps required to convert mat to a zero matrix or -1 if you cannot.

    Binary matrix is a matrix with all cells equal to 0 or 1 only.

    Zero matrix is a matrix with all cells equal to 0.

     

    Example 1:

    Input: mat = [[0,0],[0,1]]
    Output: 3
    Explanation: One possible solution is to flip (1, 0) then (0, 1) and finally (1, 1) as shown.
    

    Example 2:

    Input: mat = [[0]]
    Output: 0
    Explanation: Given matrix is a zero matrix. We don't need to change it.
    

    Example 3:

    Input: mat = [[1,1,1],[1,0,1],[0,0,0]]
    Output: 6
    

    Example 4:

    Input: mat = [[1,0,0],[1,0,0]]
    Output: -1
    Explanation: Given matrix can't be a zero matrix
    

     

    Constraints:

    • m == mat.length
    • n == mat[0].length
    • 1 <= m <= 3
    • 1 <= n <= 3
    • mat[i][j] is 0 or 1.

    Solution 1. DFS + Backtracking + Memoization

    For each cell, either we do not flip it at all or we flip it one time. There is no point flipping any cell more than one time. This means we have 2^(m * n) possible states. m and n are at most 3, so we can check each possible state reachable from the initial state and get the min cost to transit from the initial state to the final 0 state.

    Info bookkeeping

    1. To avoid computing the same state more than once, we create a dp array. dp[i] is the minimum flips needed to transit from state i to state 0.

    2. If a flip causes state go from i1 to i2 and i2 has been computed before, this means we just entered a cycle, wasted some flips and made no state transition progress. So we need to have a boolean array seen to track if we have seen a state before. If we have, return -1 to indicate the flips that led to this cycle is not going to give us a possible answer.

    Algorithm

    1. initialize dp and seen array, dp[0] = 0;

    2. starting from the initial state, try out each different cell to flip, recursively solve all different next states, then get the minimum cost among all possible next states. The recursion method is defined as follows.

    a. if a state has been seen before, return -1 to indicate the previous flip picks is not going to yield an answer;

    b. else if a state has been computed and does have a valid answer to it, return it without re-computing it;

    c. otherwise, we need to compute this state using backtracking. Mark this state as seen, then for each possible next state, do a flip that leads to the next state, recursively solve for this next state, then flip the same cell back to finish the backtracking. After solving all possible next states and updating the current state's result, mark the current state as unseen to finish backtracking. 

    The runtime is O(2^(m * n)) since we only check each state once.

    class Solution {
        private int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
        private int[] dp;   //dp[i]: the minimum flips needed transitioning from state i to state 0
        private boolean[] seen;
        public int minFlips(int[][] mat) {
            int m = mat.length, n = mat[0].length, ans = Integer.MAX_VALUE;
            dp = new int[1 << (m * n)];
            seen = new boolean[1 << (m * n)];
            Arrays.fill(dp, Integer.MAX_VALUE);
            dp[0] = 0;
          
            return dfs(mat);
        }
        private int dfs(int[][] mat) {
            int state = getState(mat);
            if(seen[state]) {
                return -1;
            }
            if(dp[state] < Integer.MAX_VALUE) {
                return dp[state];
            }   
            seen[state] = true;
            int minCost = Integer.MAX_VALUE;
            for(int i = 0; i < mat.length; i++) {
                for(int j = 0; j < mat[0].length; j++) {
                    flipAt(mat, i, j);
                    int currMin = dfs(mat);
                    if(currMin >= 0) {
                        minCost = Math.min(minCost, currMin);
                    }
                    flipAt(mat, i, j);
                }
            }
            dp[state] = (minCost == Integer.MAX_VALUE ? -1 : minCost + 1);
            seen[state] = false;        
            return dp[state];        
        }
        
        private void flipAt(int[][] mat, int x, int y) {
            mat[x][y] = (mat[x][y] == 0 ? 1 : 0);
            for(int dir = 0; dir < 4; dir++) {
                int x1 = x + dx[dir];
                int y1 = y + dy[dir];
                if(x1 >= 0 && x1 < mat.length && y1 >= 0 && y1 < mat[0].length) {
                    mat[x1][y1] = (mat[x1][y1] == 0 ? 1 : 0);
                }
            }
        }
        private int getState(int[][] mat) {
            int state = 0, cnt = 0;
            for(int i = 0; i < mat.length; i++) {
                for(int j = 0; j < mat[0].length; j++) {
                    state = (state | (mat[i][j] << cnt));
                    cnt++;
                }
            }
            return state;
        }
    }

    Solution 2. BFS + Memoization + Awesome bit operations

    Another way of solving this problem is to use bfs. Since the problem asks for the minimum number of flips to get to state 0, we can treat a flip from state S1 to state S2 as traveling from node S1 to S2 in one step. Just like in solution 1, we still track each state's result. BFS ensures that the 1st time we update one state's result, it is guaranteed to be the minimum cost going from the initial state. We only add new states to the bfs queue in this case. If we later get a bigger cost for the same state, we just ignore it. This ensures that we only compute each possible state once. 

    Algorithm.

    1. use bit operations to get the initial state;

    2. init all states to be Integer.MAX_VALUE and initial state to have cost 0.

    3. add the initial state to queue and do bfs: if reaching state 0, return its min cost; otherwise try all possible flips at different cells by using xor operations, if the next state is computed the first time, add it to the queue.

    4. return -1 if all states exhausted without success.

    The runtime is also O(2^(m * n)), but it is faster thanks to the clear bit operations.

    class Solution {
        public int minFlips(int[][] mat) {
            int m = mat.length;
            int n = mat[0].length;
            int start = 0;
            
            for(int i = 0; i < m * n; ++i)
                start += mat[i / n][i % n] << i;
            int[] dist = new int[1<<(m * n)];
            Arrays.fill(dist, Integer.MAX_VALUE);
    
            dist[start] = 0;
            ArrayDeque<Integer> q = new ArrayDeque<>();
            q.add(start);
            while(!q.isEmpty()) {
                int curr = q.pollFirst();    
                int d = dist[curr];    
                if(curr == 0) {
                    return d;
                }
    
                for (int i = 0; i < m * n; ++i) {
                    int x = i / n;
                    int y = i % n;
                    int next = curr;  
                    next ^= 1 << i;  
                    if(y > 0) {
                        next ^= 1 << (i - 1);
                    }
                    if(y < n - 1) {
                        next ^= 1 << (i + 1);
                    }
                    if(x > 0) {
                        next ^= 1 << (i - n);
                    }
                    if(x < m - 1) {
                        next ^= 1 << (i + n);
                    }
                    
                    if(d + 1 < dist[next]) {
                        dist[next] = d+1;
                        q.add(next);
                    }
                }
            }
            return -1;
        }
    }
  • 相关阅读:
    elementUI的table分页多选,记住上一页并勾选中,:row-key的使用方法
    如何在vue中使用svg
    父子组件传值,子组件接收不到值,并且无法动态更改video的视频源进行视频播放
    vue项目中如何使用dataform向后台传值
    'eslint'不是内部或外部命令,也不是可运行的程序
    小程序点击分享open-type="share"触发父元素怎么解决?
    vue项目启动报错Module build failed: Error: No PostCSS Config found in:
    eslint在webstorm中有错误警告
    微信小程序 image图片组件实现宽度固定 高度自适应
    JAVA设计模式学习--代理模式
  • 原文地址:https://www.cnblogs.com/lz87/p/12005423.html
Copyright © 2011-2022 走看看