Hard

题目描述

给定一个 m x n 的二进制矩阵 mat。在一步操作中,你可以选择一个单元格并翻转它以及它的四个相邻单元格(如果存在的话)。翻转的意思是将 1 变成 0,将 0 变成 1。两个单元格如果共享一条边,则称它们为相邻单元格。

返回将 mat 转换为零矩阵所需的最少步数,如果无法转换则返回 -1

二进制矩阵是只包含 01 的矩阵。

零矩阵是所有单元格都等于 0 的矩阵。

示例 1:

输入:mat = [[0,0],[0,1]]
输出:3
解释:一个可能的解决方案是先翻转 (1,0),然后翻转 (0,1),最后翻转 (1,1)。

示例 2:

输入:mat = [[0]]
输出:0
解释:给定的矩阵已经是零矩阵,我们不需要改变它。

示例 3:

输入:mat = [[1,0,0],[1,0,0]]
输出:-1
解释:给定的矩阵无法转换为零矩阵。

约束条件:

  • m == mat.length
  • n == mat[i].length
  • 1 <= m, n <= 3
  • mat[i][j]01

提示:

  • 翻转同一个位置两次等于没有翻转。每个位置最多只需要翻转一次。尝试所有可能的组合。时间复杂度 O(2^(n*m))。

解题思路

这道题有两种主要解法:暴力枚举BFS

方法一:暴力枚举(推荐)

根据题目提示,翻转同一个位置两次等于没有翻转,所以每个位置最多只需要翻转一次。对于 m×n 的矩阵,我们有 2^(m×n) 种翻转组合。

我们可以用一个整数的二进制表示来表示翻转方案,第 i 位为 1 表示翻转第 i 个位置。对于每种翻转方案,我们模拟执行翻转操作,检查最终是否能得到零矩阵。

翻转操作会影响当前位置及其上下左右四个相邻位置(如果存在)。

方法二:BFS

将矩阵状态看作一个节点,用 BFS 搜索从初始状态到零矩阵状态的最短路径。矩阵状态可以用整数编码,便于去重。

由于约束条件限制了矩阵大小最大为 3×3,状态空间不超过 2^9 = 512,所以两种方法都是可行的。暴力枚举实现更简单,推荐使用。

代码实现

class Solution {
public:
    int minFlips(vector<vector<int>>& mat) {
        int m = mat.size(), n = mat[0].size();
        int total = m * n;
        int ans = INT_MAX;
        
        // 枚举所有可能的翻转组合
        for (int mask = 0; mask < (1 << total); mask++) {
            // 复制原矩阵
            vector<vector<int>> grid = mat;
            
            // 根据mask执行翻转操作
            for (int i = 0; i < total; i++) {
                if (mask & (1 << i)) {
                    int row = i / n, col = i % n;
                    flip(grid, row, col, m, n);
                }
            }
            
            // 检查是否为零矩阵
            if (isZeroMatrix(grid)) {
                ans = min(ans, __builtin_popcount(mask));
            }
        }
        
        return ans == INT_MAX ? -1 : ans;
    }
    
private:
    void flip(vector<vector<int>>& grid, int r, int c, int m, int n) {
        int dirs[5][2] = {{0, 0}, {-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        for (int i = 0; i < 5; i++) {
            int nr = r + dirs[i][0], nc = c + dirs[i][1];
            if (nr >= 0 && nr < m && nc >= 0 && nc < n) {
                grid[nr][nc] ^= 1;
            }
        }
    }
    
    bool isZeroMatrix(const vector<vector<int>>& grid) {
        for (const auto& row : grid) {
            for (int val : row) {
                if (val == 1) return false;
            }
        }
        return true;
    }
};
class Solution:
    def minFlips(self, mat: List[List[int]]) -> int:
        m, n = len(mat), len(mat[0])
        total = m * n
        ans = float('inf')
        
        # 枚举所有可能的翻转组合
        for mask in range(1 << total):
            # 复制原矩阵
            grid = [row[:] for row in mat]
            
            # 根据mask执行翻转操作
            for i in range(total):
                if mask & (1 << i):
                    row, col = i // n, i % n
                    self.flip(grid, row, col, m, n)
            
            # 检查是否为零矩阵
            if self.is_zero_matrix(grid):
                ans = min(ans, bin(mask).count('1'))
        
        return ans if ans != float('inf') else -1
    
    def flip(self, grid, r, c, m, n):
        dirs = [(0, 0), (-1, 0), (1, 0), (0, -1), (0, 1)]
        for dr, dc in dirs:
            nr, nc = r + dr, c + dc
            if 0 <= nr < m and 0 <= nc < n:
                grid[nr][nc] ^= 1
    
    def is_zero_matrix(self, grid):
        return all(val == 0 for row in grid for val in row)
public class Solution {
    public int MinFlips(int[][] mat) {
        int m = mat.Length, n = mat[0].Length;
        int total = m * n;
        int ans = int.MaxValue;
        
        // 枚举所有可能的翻转组合
        for (int mask = 0; mask < (1 << total); mask++) {
            // 复制原矩阵
            int[][] grid = new int[m][];
            for (int i = 0; i < m; i++) {
                grid[i] = new int[n];
                Array.Copy(mat[i], grid[i], n);
            }
            
            // 根据mask执行翻转操作
            for (int i = 0; i < total; i++) {
                if ((mask & (1 << i)) != 0) {
                    int row = i / n, col = i % n;
                    Flip(grid, row, col, m, n);
                }
            }
            
            // 检查是否为零矩阵
            if (IsZeroMatrix(grid)) {
                ans = Math.Min(ans, CountBits(mask));
            }
        }
        
        return ans == int.MaxValue ? -1 : ans;
    }
    
    private void Flip(int[][] grid, int r, int c, int m, int n) {
        int[,] dirs = {{0, 0}, {-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        for (int i = 0; i < 5; i++) {
            int nr = r + dirs[i, 0], nc = c + dirs[i, 1];
            if (nr >= 0 && nr < m && nc >= 0 && nc < n) {
                grid[nr][nc] ^= 1;
            }
        }
    }
    
    private bool IsZeroMatrix(int[][] grid) {
        foreach (var row in grid) {
            foreach (int val in row) {
                if (val == 1) return false;
            }
        }
        return true;
    }
    
    private int CountBits(int n) {
        int count = 0;
        while (n != 0) {
            count++;
            n &= n - 1;
        }
        return count;
    }
}
var minFlips = function(mat) {
    const m = mat.length;
    const n = mat[0].length;
    
    const matrixToState = (matrix) => {
        let state = 0;
        for (let i = 0; i < m; i++) {
            for (let j = 0; j < n; j++) {
                if (matrix[i][j] === 1) {
                    state |= (1 << (i * n + j));
                }
            }
        }
        return state;
    };
    
    const flip = (state, row, col) => {
        const directions = [[0, 0], [-1, 0], [1, 0], [0, -1], [0, 1]];
        for (const [dr, dc] of directions) {
            const nr = row + dr;
            const nc = col + dc;
            if (nr >= 0 && nr < m && nc >= 0 && nc < n) {
                state ^= (1 << (nr * n + nc));
            }
        }
        return state;
    };
    
    const initialState = matrixToState(mat);
    if (initialState === 0) return 0;
    
    const queue = [initialState];
    const visited = new Set([initialState]);
    let steps = 0;
    
    while (queue.length > 0) {
        const size = queue.length;
        steps++;
        
        for (let i = 0; i < size; i++) {
            const currentState = queue.shift();
            
            for (let row = 0; row < m; row++) {
                for (let col = 0; col < n; col++) {
                    const nextState = flip(currentState, row, col);
                    
                    if (nextState === 0) return steps;
                    
                    if (!visited.has(nextState)) {
                        visited.add(nextState);
                        queue.push(nextState);
                    }
                }
            }
        }
    }
    
    return -1;
};

复杂度分析

复杂度类型复杂度说明
时间复杂度O(2^(m×n) × m×n)枚举所有 2^(m×n) 种翻转组合,每次需要 O(m×n) 时间进行翻转和检查
空间复杂度O(m×n)需要复制矩阵进行模拟

相关题目