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;
        }
    }
  • 相关阅读:
    如何完成看似不可能完成的任务
    SQL Server 2008 数据挖掘算法
    混在北京
    09年的一个方案,很遗憾没有采纳,回头看看,我还认为我是对的
    Metro Home
    InputScope
    Mozart Update 3 (7392…)
    搏斗Mango beta…
    Mango 7712 is coming
    HD2 update NODO
  • 原文地址:https://www.cnblogs.com/lz87/p/12005423.html
Copyright © 2011-2022 走看看