861.翻转矩阵后的得分
有一个二维矩阵 A 其中每个元素的值为 0 或 1 。
移动是指选择任一行或列,并转换该行或列中的每一个值:将所有 0 都更改为 1,将所有 1 都更改为 0。
在做出任意次数的移动后,将该矩阵的每一行都按照二进制数来解释,矩阵的得分就是这些数字的总和。
返回尽可能高的分数。
示例:
输入:[[0,0,1,1],[1,0,1,0],[1,1,0,0]]
输出:39
解释:
转换为 [[1,1,1,1],[1,0,0,1],[1,1,1,1]]
0b1111 + 0b1001 + 0b1111 = 15 + 9 + 15 = 39
提示:
1 <= A.length <= 20
1 <= A[0].length <= 20
A[i][j] 是 0 或 1
解决这个问题首先要解决什么呢?就是找到什么时候返回值最大,毫无疑问,最高位最大时整个数最大,也就是说,整个矩阵首列都要为1,所以遍历整个矩阵首列,如果该位为0,就把该行翻转(翻转操作对1异或即可)。
接下来处理首列之外的数据,因为第一列已经为1,所以不能进行行翻转了,就遍历之后的列,如果0多就进行翻转。
处理完数据之后,矩阵就变成了我们想要的矩阵,从第一列开始,把整列加起来,然后依次乘2加列和,最后得到的结果就是我们要的值,
class Solution {
//要想值最大,第一列必须都为1,所以先遍历每一行,把首位为0的翻转
//然后从第二列开始,如果是0多久翻转,1多就不变
public int matrixScore(int[][] A) {
int res = 0;
//对首位为0的行进行翻转
for(int i = 0; i < A.length; i++) {
if(A[i][0] == 0) {
reverse(A[i]);
}
}
//处理第一列之后的数据,按列来处理
for(int j = 1; j < A[0].length; j++) {
int num = 0;
for(int i = 0; i < A.length; i++) {
if(A[i][j] == 0) {
num++;
}
if(num > A.length/2) {
for(int k = 0; k < A.length; k++) {
A[k][j] ^= 1;
}
break;
}
}
}
//计算结果
for(int j = 0; j < A[0].length; j++) {
res = res << 1;
for(int i = 0; i < A.length; i++) {
res += A[i][j];
}
}
return res;
}
public void reverse(int[] arr) {
for(int i = 0; i < arr.length; i++) {
arr[i] ^= 1;
}
}
}
但是这样处理并不是最优解,因为我们要想想我们要的结果是什么,只是一个最大值。但是在求这个最大值的过程中我们对矩阵做了很多没必要的操作,于是对解法进行优化。
第一步还是不变,对首位为0的行进行翻转。
重点在第二步和求和的过程中,我们开始做的是如果0的个数大于列长度的一半,就翻转,这个操作的目的是把列中大多数变成1,最后相加。但是仔细一想,最后都是1相加,这个1的个数就是一列中0和1数量的较大值,所以我们没有必要对0进行翻转,只是计算数量即可,并且在计算数量的过程中就可以求出返回值。
class Solution {
//看到的新解法
//首先把第一列全部变成1,res加上行数(因为第一列全部为1)
//然后再遍历之后的每一列,0和1比较大的那个数量就是我们要的(省去了翻转的过程)
//res * 2 再加上这个数量,遍历完就是我们要的结果
public int matrixScore(int[][] A) {
int res = 0;
res += A.length;
for(int i = 0; i < A.length; i++) {
if(A[i][0] == 0) {
for(int j = 0; j < A[0].length; j++) {
A[i][j] ^= 1;
}
}
}
for(int j = 1; j < A[0].length; j++) {
int num = 0;
for(int i = 0; i < A.length; i++) {
if(A[i][j] == 1) {
num++;
}
}
num = Math.max(num, A.length-num);
res = (res << 1) + num;
}
return res;
}
}