题目链接 https://leetcode.com/problems/minimum-number-of-flips-to-convert-binary-matrix-to-zero-matrix/
大致题意
给你一个 m x n 的二进制矩阵 mat,每一步,你可以选择一个单元格并将它反转(反转表示 0 变 1 ,1 变 0 )。如果存在和它相邻的单元格,那么这些相邻的单元格也会被反转。(注:相邻的两个单元格共享同一条边。)
请你返回将矩阵 mat 转化为全零矩阵的 最少反转次数,如果无法转化为全零矩阵,请返回 -1。
二进制矩阵的每一个格子要么是 0 要么是 1 ,全零矩阵是所有格子都为 0 的矩阵。
具体例子见原题链接
题解:首先考虑的是暴力求解,复杂度O(mn⋅2^mn),暴力枚举哪些位置需要反转,一共有 2^mn 种方案,对于每种方案,验证最终是否为全 0,找到反转次数最少的方案。
但是随后可以发现我们发现,如果确定了第一行(或第一列)反转哪些位置,则之后反转的位置就都可以确定,举个例子,假设我们已经反转了第一行的某些位置,我们从第二行开始一直到最后一行,如果发现当前位置的上一行是 1,则当前位置需要进行反转,简单来说就是,对第一行遍历所有可能,也就是对每一个位置都选择是否翻转,一共2^n种可能,然后从第二行开始,一单发现某一个位置上边的位置是1,那么我们就翻转这个格子,这样持续下去可以确保每行执行完,该行以上的全是0,判断是否全部翻转为0的依据是判断下最后一行是否也都已经变成了 0。
这样上述方案的时间复杂度是O(m*n*2^n),时间上大大缩小了
下边是实现代码,配有充足的注释
class Solution {
public:
int minFlips(vector<vector<int>>& mat) {
int m = mat.size(),n = mat[0].size();
//要翻转的五个位置数组
int dx[5] = {0,0,1,0,-1};
int dy[5] = {0,1,0,-1,0};
//最后的返回结果,初始化为一个大值
int ans = m*n+1;
//K从0到1<<n,一共2^n
for(int K = 0;K<(1<<n);K++){
//对于每一个K定义一个局部结果
int cnt = 0;
//接下来定义一个temp数组,这里要说的是,我们可以对
//原数组翻转成0也可以将0数组翻转成原数组
//这二者是等价的
vector<vector<int>>temp(m,vector<int>(n,0));
//开始处理第一行
for(int j = 0;j<n;j++){
if((K>>j)&1){
cnt++;
for(int k = 0;k<5;k++){
int x = 0+dx[k],y = j+dy[k];
if(x<0||x>=m||y<0||y>=n)continue;
//翻转全部和1异或
temp[x][y]^=1;
}
}
}
//处理后边行
for(int i = 1;i<m;i++)
for(int j = 0;j<n;j++){
//发现上边位置需要翻转
if(temp[i-1][j]^mat[i-1][j]){
cnt++;
for(int k = 0;k<5;k++){
int x = i+dx[k],y = j+dy[k];
if(x<0||x>=m||y<0||y>=n)continue;
temp[x][y]^=1;
}
}
}
int flag = 1;
//检验最后一行是否为0
for(int j = 0;j<n;j++)
if(temp[m-1][j]^mat[m-1][j]){
flag = 0;
break;
}
//满足条件取小值
if(flag)ans = min(ans,cnt);
}
//没有满足条件的情况下返回-1
if(ans==m*n+1)ans = -1;
return ans;
}
};
因为按位操作方面比较薄弱,所以把这个记录在这里,供以后自己复习或者其他人学习用,运行结果比想象中要好一些
参考链接:
https://leetcode.com/problems/minimum-number-of-flips-to-convert-binary-matrix-to-zero-matrix/