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,所以从第二列开始遍历,如果该列 0 的数量大于 1 的数量,则翻转这一列。
- 第三步计算结果
方法一:贪心算法
按照上述步骤实现的,优化版本见下
public int matrixScore(int[][] A) {
int rowNum = A.length;
int cloNum = A[0].length;
// 先找第 1 列,如果为 0 翻转这一行
for (int i = 0; i < rowNum; i++) {
if (A[i][0] == 0) {
reverse(A, i , true);
}
}
// 从第 2 列开始,计算该列为 0 的个数,大于一半则翻转这一列
for (int j = 1; j < cloNum; j++) {
int count = 0;
for (int i = 0; i < rowNum; i++) {
if (A[i][j] == 0) {
count++;
}
}
if (count > rowNum / 2) {
reverse(A, j, false);
}
}
// 计算结果
int ans = 0;
for (int i = 0; i < rowNum; i++) {
StringBuffer sb = new StringBuffer();
for (int j = 0; j < cloNum; j++) {
sb.append(A[i][j]);
}
ans += Integer.parseInt(sb.toString(), 2);
}
return ans;
}
// 翻转行或列 row为ture表示翻转行,反之表示翻转列
public void reverse(int[][] A, int num, boolean row) {
if (row) {
for (int i = 0; i < A[0].length; i++) {
A[num][i] = 1 - A[num][i];
}
} else {
for (int i = 0; i < A.length; i++) {
A[i][num] = 1 - A[i][num];
}
}
}
优化版本:思路是完全一致的,实现的方式要简洁很多
- 没有必要真正的进行翻转,只需要按照贪心的思想统计即可
- 计算结果使用位运算 累加每一列的结果,每一列 1 的数量用贪心的思想求出
public int matrixScore1(int[][] A) {
int m = A.length, n = A[0].length;
// 计算第一列的结果
int ret = m * (1 << (n - 1));
// 累加余下每一列的结果
for (int j = 1; j < n; j++) {
// 该列 1 的数量,没有翻转累加 A[i][j],翻转过累加 (1 - A[i][j])
int nOnes = 0;
for (int i = 0; i < m; i++) {
if (A[i][0] == 1) {
nOnes += A[i][j];
} else {
nOnes += (1 - A[i][j]);
}
}
// 计算 1 和 0 的较大者,即为该列翻转后 1 的数量
int k = Math.max(nOnes, m - nOnes);
ret += k * (1 << (n - 1 - j));
}
return ret;
}