LeetCode 1284 - Minimum Number of Flips to Convert Binary Matrix to Zero Matrix


https://leetcode.com/problems/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.
https://leetcode.com/problems/minimum-number-of-flips-to-convert-binary-matrix-to-zero-matrix/discuss/446306/C%2B%2B-Bit-vector-%2B-Regular-BFS

https://leetcode.com/problems/minimum-number-of-flips-to-convert-binary-matrix-to-zero-matrix/discuss/446342/JavaPython-3-Convert-matrix-to-int-then-BFS-w-explanation-comments-and-analysis.
Q: Could you please further explain about the logic of transferring the matrix to int?
sum(cell << (i * n + j) for i, row in enumerate(mat) for j, cell in enumerate(row))
I wonder how it works and why it works.
A: For Input: mat = [[0,0],[0,1]], map it to 0b1000, that is, mapping mat[i][j] to the (i * n + j)th bit of an int. specifically,
mat[0][0] = 0, corresponds to 0th bit, which is 0;
mat[0][1] = 0, corresponds to 1st bit, which is 0;
mat[1][0] = 0, corresponds to 2nd bit, which is 0;
mat[1][1] = 1, corresponds to 3rd bit, which is 1;
After mapping, any operations on the binary cells of the mat are equals to the operations on the corresponding bits of the mapped int. That's why it works.
Q: Why do you use "|" to initialize the matrix and use "^" to calculate the next?
A:
  1. When using 0 |= b, where b = 0 or 1, the result is b; you can change it to
start  += mat[i][j] << (i * n + j); 
  1. Use next ^ 1 << k (where k = i * n + j) to flip kth bit of next, which is equal to flipping the corresponding cell (i, j) in the matrix.
end of Q & A

  1. Since m < 3, n < 3 are given as constraints, there are at most 9 cells and an int has enough bits to store their values;
  2. Map the m * n cells of the initial state of the matrix to the 0 ~ m * n - 1th bits of an int: start;
  3. For each one of the m * n bits, flip it and its neighbors, then BFS to check if 0, corresponding to an all 0s matrix, is among the resulting states; if yes, return the minimum steps needed;
  4. Use a Set to avoid duplicates;
  5. If after the traversal of all states without finding 0, return -1.
    private static final int[] d = {0, 0, 1, 0, -1, 0};
    public int minFlips(int[][] mat) {
        int start = 0, m = mat.length, n = mat[0].length;
        for (int i = 0; i < m; ++i)
            for (int j = 0; j < n; ++j)
                start |= mat[i][j] << (i * n + j); // convert the matrix to an int.
        Queue<Integer> q = new LinkedList<>(Arrays.asList(start));
        Set<Integer> seen = new HashSet<>(q);
        for (int step = 0; !q.isEmpty(); ++step) {
            for (int sz = q.size(); sz > 0; --sz) {
                int cur = q.poll();
                if (cur == 0) // All 0s matrix found.
                    return step;
                for (int i = 0; i < m; ++i) { // traverse all m * n bits of cur.
                    for (int j = 0; j < n; ++j) {
                        int next = cur;
                        for (int k = 0; k < 5; ++k) { // flip the cell (i, j) and its neighbors.
                            int r = i + d[k], c = j + d[k + 1];
                            if (r >= 0 && r < m && c >= 0 && c < n)
                                next ^= 1 << (r * n + c);
                        }
                        if (seen.add(next)) // seen it before ?
                            q.offer(next); // no, put it into the Queue.
                    }
                }    
            }
        }
        return -1; // impossible to get all 0s matrix.
    }
    def minFlips(self, mat: List[List[int]]) -> int:
        m, n = len(mat), len(mat[0])
        start = sum(cell << (i * n + j) for i, row in enumerate(mat) for j, cell in enumerate(row))
        dq = collections.deque([(start, 0)])
        seen = {start}
        while dq:
            cur, step = dq.popleft()
            if not cur:
                return step
            for i in range(m):
                for j in range(n):
                    next = cur
                    for r, c in (i, j), (i, j + 1), (i, j - 1), (i + 1, j), (i - 1, j):
                        if m > r >= 0 <= c < n:
                            next ^= 1 << (r * n + c)
                    if next not in seen:
                        seen.add(next)
                        dq.append((next, step + 1))
        return -1
Analysis:
Time & Space: O(2 ^ (m * n))

X.
https://leetcode.com/problems/minimum-number-of-flips-to-convert-binary-matrix-to-zero-matrix/discuss/446371/Java-Recursion-%2B-Memoization-Explained
Recursion + Memoization
In this problem, we can think in a brute manner first.
We will try to flip the element at each index of the 2D matrix once and let recursion get the answer for the flipped array.
  • Now while doing this we need to take care that we don't get trapped in a cycle (I have taken care of it by using a hashset).
  • For eg consider the 2D matrix given below.
    [1 0]
    [1 0]
  • Initially the call will be made by flipping the (0, 0) indexed element and its neighbors according to what is given in question.
  • The 2D matrix changes to :-
    [0 1]
    [0 0]
  • Now this configuration of the 2D array will make a recursive call by flipping the element at index (0, 0) and its neighbors. This will again give us an array configuration we had seen before in the same recursive branh (a cycle).
    [1 0]
    [1 0]
  • To avoid cycles i have used a set.
    Since the constraints of the problem are very weak, so using a string to memoize the state would not cost much on the runtime though concatenating strings is expensive.
    In this problem I have tried all possibilities of flipping all elements in a 2D array and made recursive calls to the resulting configurations. The recursive branch which brings the minimum no of steps will be a part of the solution.
    In the base case we check if all elements in the array are 0 or not.
    (BFS is another way to come up with a solution in which you will be needing a visited array(to avoid cycle) and a queue).
class Solution {
    
    public static boolean check(int[][] mat, int n, int m){
        for(int i = 0; i < n; i++){
            for(int j = 0; j < m; j++){
                if(mat[i][j] == 1) return false;
            }
        }
        return true;
    }
    
    public static void flip(int[][] mat, int n, int m, int i, int j){
        mat[i][j] = mat[i][j] ^ 1;
        if(i - 1 >= 0) mat[i - 1][j] = mat[i - 1][j] ^ 1;
        if(j - 1 >= 0) mat[i][j - 1] = mat[i][j - 1] ^ 1;
        if(i + 1 < n) mat[i + 1][j] = mat[i + 1][j] ^ 1;
        if(j + 1 < m) mat[i][j + 1] = mat[i][j + 1] ^ 1;
    }
    
    public static int func(int[][] mat, int n, int m, HashSet<String> set, HashMap<String, Integer> dp){
        if(check(mat, n, m)) return 0;
        String t = "";
        for(int i = 0; i < n; i++){
            for(int j = 0; j < m; j++){
                t += Integer.toString(mat[i][j]);
            }
        }
        
        if(dp.containsKey(t)) return dp.get(t);
        if(set.contains(t)) return Integer.MAX_VALUE;
        
        set.add(t);
        int min = Integer.MAX_VALUE;
        for(int i = 0; i < n; i++){
            for(int j = 0; j < m; j++){
                flip(mat, n, m, i, j);
                int small = func(mat, n, m, set, dp);
                if(small != Integer.MAX_VALUE) min = Math.min(min, 1 + small);
                flip(mat, n, m, i, j);
            }
        }
        set.remove(t);
        dp.put(t, min);
        return min;
    }
    
    public int minFlips(int[][] mat) {
        int n = mat.length, m = mat[0].length;
        HashMap<String, Integer> dp = new HashMap<>();
        int ans = func(mat, n, m, new HashSet<>(), dp);
        return ans == Integer.MAX_VALUE ? -1 : ans;
    }

x. https://leetcode.com/problems/minimum-number-of-flips-to-convert-binary-matrix-to-zero-matrix/discuss/446456/Beat-100-No-NP-hard-O((nm)3)-linear-algebra-solution
This is a simplified version of a google phone screen posted below:
https://leetcode.com/discuss/interview-question/422725/google-phone-screen-lights-out-puzzle/397815
The simplification here mainly refers to the fairly small constraints. With m and n both <=3, we can come up with brute force exponential solution.
But this is actually not a NP-hard problem in maths. Some good explanation can be found below:
https://www.youtube.com/watch?v=oCHCD_-nhg4
http://mathworld.wolfram.com/LightsOutPuzzle.html


https://www.acwing.com/solution/LeetCode/content/6856/
(枚举第一行) O(mn2n)
  1. 我们发现,如果确定了第一行(或第一列)反转哪些位置,则之后反转的位置就都可以确定。
  2. 假设我们已经反转了第一行的某些位置,我们从第二行开始一直到最后一行,如果发现当前位置的上一行是 1,则当前位置需要进行反转。我们在之后每一行,都去填上一行留下的坑。
  3. 最后判断下最后一行是否都已经变成了 0。

时间复杂度

  • 仅需要枚举第一行的方案,然后进行整体的判断,故时间复杂度为 O(mn2n)
  • 如果列数较小,行数较大,则可以枚举第一列。

空间复杂度

  • 需要额外 O(mn) 的空间记录中间过程。
    int minFlips(vector<vector<int>>& mat) {
        int m = mat.size(), n = mat[0].size();

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

        int ans = m * n + 1;

        for (int S = 0; S < (1 << n); S++) {
            int tot = 0;
            vector<vector<int>> tmp(m, vector<int>(n, 0));

            for (int j = 0; j < n; j++)
                if ((S >> j) & 1) {
                    tot++;

                    for (int k = 0; k < 5; k++) {
                        int tx = 0 + dx[k], ty = j + dy[k];
                        if (tx < 0 || tx >= m || ty < 0 || ty >= n)
                            continue;
                        tmp[tx][ty] ^= 1;
                    }
                }

            for (int i = 1; i < m; i++)
                for (int j = 0; j < n; j++)
                    if (mat[i - 1][j] ^ tmp[i - 1][j]) {
                        tot++;

                        for (int k = 0; k < 5; k++) {
                            int tx = i + dx[k], ty = j + dy[k];
                            if (tx < 0 || tx >= m || ty < 0 || ty >= n)
                                continue;
                            tmp[tx][ty] ^= 1;
                        }
                    }

            bool flag = true;
            for (int j = 0; j < n; j++)
                if (mat[m - 1][j] ^ tmp[m - 1][j]) {
                    flag = false;
                    break;
                }

            if (flag)
                ans = min(ans, tot);
        }

        if (ans == m * n + 1)
            ans = -1;

        return ans;
    }

(暴力枚举) O(mn2mn)
  1. 暴力枚举哪些位置需要反转,一共有 2mn 种方案。
  2. 对于每种方案,验证最终是否为全 0。
  3. 找到反转次数最少的方案。

时间复杂度

  • 验证需要 O(mn) 的时间复杂度,故总时间复杂度为 O(mn2mn)

空间复杂度

  • 需要额外 O(mn) 的空间,可以通过恢复现场来省略掉这部分空间
    int minFlips(vector<vector<int>>& mat) {
        int m = mat.size(), n = mat[0].size();

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

        int ans = m * n + 1;

        for (int S = 0; S < (1 << m * n); S++) {
            int tot = 0;
            vector<vector<int>> tmp(m, vector<int>(n, 0));

            for (int i = 0; i < m * n; i++)
                if ((S >> i) & 1) {
                    tot++;
                    int x = i / n, y = i % n;

                    for (int j = 0; j < 5; j++) {
                        int tx = x + dx[j], ty = y + dy[j];
                        if (tx < 0 || tx >= m || ty < 0 || ty >= n)
                            continue;
                        tmp[tx][ty] ^= 1;
                    }
                }

            bool flag = true;
            for (int i = 0; i < m && flag; i++)
                for (int j = 0; j < n && flag; j++) {
                    if (tmp[i][j] ^ mat[i][j])
                        flag = false;
                }
            if (flag)
                ans = min(ans, tot);
        }
        if (ans == m * n + 1)
            ans = -1;

        return ans;
    }




Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts