矩阵元素相乘
A[ n, m ] 是一个 n 行 m 列的矩阵,a[ i, j ] 表示 A 的第 i 行 j 列的元素,定义 x[ i , j ] 为 A 的第 i 行和第 j 列除了 a[ i, j ] 之外所有元素(共 n + m - 2 个)的乘积,即 x[ i, j ] = a[ i, 1 ] * a[ i, 2 ] * ... * a[ i, j-1 ] * ... * a[ i, m ] * a[ 1, j ] * a[ 2, j ] * ... * a[ i-1, j ] * a[ i+1, j ] * ...* a[ n, j ] 。现输入非负整形的矩阵 A[ n, m ] ,求 MAX( x[ i, j ] ),即所有的 x[ i, j ] 中的最大值。
原题链接:矩阵元素相乘_牛客题霸_牛客网 (nowcoder.com)
格式
输入描述:第一行两个整数 n 和 m 。之后 n 行输入矩阵,均为非负整数。
输出描述:一行输出答案。
样例
输入:3 5
5 1 8 5 2
1 3 10 3 3
7 8 5 5 16输出:358400
思路分析
首先,我们需要理解题目中的 x[i,j]
是什么。它表示的是矩阵 A 的第 i 行和第 j 列中除了 a[i,j]
之外的所有元素的乘积。那么,我们可以思考,如何高效地计算出每一个 x[i,j]
的值,而不需要对每个 x[i,j]
都进行 n+m-2
次乘法操作。
观察 x[i,j]
的定义,我们可以发现,它其实是由两部分乘积构成的:一部分是第 i 行的所有元素的乘积除以 a[i,j]
,另一部分是第 j 列的所有元素的乘积除以 a[i,j]
。如果我们能够提前计算出每一行和每一列的乘积,那么计算 x[i,j]
就会变得非常简单。
int num = (row[i]/arr[i][j])*(col[j]/arr[i][j]);
但是,这里有一个特殊情况需要注意:如果 a[i,j]
是 0 ,但是,我们不能简单地将包含 0 的行或列的乘积设为 0 ,因为其他位置的 x[i,j]
可能还需要用到这些乘积(只是不包括 a[i,j]
为 0 的那个位置)。因此我们遇到这种情况应该跳过去。
for (int i = 0;i < n;i++) {
for (int j = 0;j < m;j++) {
arr[i][j] = in.nextInt();
// 计算行列 0 的个数,为 0 时不算入乘积
if (arr[i][j] == 0) {
row0[i]++;
col0[j]++;
}else {
row[i]*= arr[i][j];
col[j]*= arr[i][j];
}
}
}
当行列的乘积都求取完毕后,我们开始遍历矩阵求取 x[i,j]
,而这时,我们又会遇到 a[i,j]
为 0 的情况,它会导致我们计算 x[i,j]
时出现除以 0 的操作,因此我们这里需要分情况去计算。
// 虽然 arr[i][j] 不参与计算,但它可能作为 0 出现在被除数中,因此分开计算
if (arr[i][j] == 0) {
ans = Math.max(ans, row[i]*col[j]);
}else {
ans = Math.max(ans, (row[i]/arr[i][j])*(col[j]/arr[i][j]));
}
到这里,我们可以发现有个问题,因为前面计算乘积时都是遇到 0 就跳过去的,虽然 a[i,j]
并不是 x[i,j]
的因子,它是否为 0 我们并不用担心,但是如果行列乘积中除了 a[i,j]
存在其他因子为 0 时,我们又怎么去判断这种情况?我们可以想到,我们只需要去计算行列乘积时因子出现 0 的次数,再减去两倍 a[i,j]
为 0 的次数,就能得到 x[i,j]
因子为 0 的个数了。因此,我们需要分别记录每一行和每一列中 0 的个数。
int [] row0 = new int[n]; // 存储每一行有几个 0
int [] col0 = new int[m]; // 存储每一列有几个 0
Arrays.fill(row, 1); // 将存储乘积的两个数组的值都赋为 1,便于计算乘积
Arrays.fill(col, 1);
具体步骤:
-
初始化两个数组
row
和col
,分别用于存储每一行和每一列(除了当前位置元素外)的乘积。同时,初始化两个数组row0
和col0
,用于记录每一行和每一列中 0 的个数。 -
遍历矩阵 A ,填充
arr
数组,并同时更新row
、col
、row0
和col0
数组。对于arr[i][j]
,如果它是 0 ,则增加row0[i]
和col0[j]
的计数;否则,将其乘到row[i]
和col[j]
上。 -
遍历矩阵 A ,计算每个
x[i,j]
的值。对于每个位置(i,j)
,首先检查row0[i]
和col0[j]
的和。如果这个和大于 0 ,说明至少有一行或一列包含 0 ,因此x[i,j]
应该是0(因为至少有一个因子是 0 )。如果这个和等于 0 ,说明既没有行也没有列包含 0 ,此时我们可以根据row[i]
和col[j]
来计算x[i,j]
的值。但是,需要注意的是,如果arr[i][j]
本身为 0 时,我们还需要将它从row[i]
和col[j]
中除去,因为x[i,j]
的定义中不包括a[i,j]
。 -
在计算过程中,不断更新最大值
ans
。
代码
import java.util.Scanner;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 题目会读入多组数据,while 循环读取输入
while (in.hasNextInt()) {
int n = in.nextInt(); // 矩阵行数
int m = in.nextInt(); // 矩阵列数
int [][] arr = new int[n][m];
int [] row = new int[n]; // 存储每一行除 0 外其他数的乘积
int [] col = new int[m]; // 存储每一列除 0 外其他数的乘积
int [] row0 = new int[n]; // 存储每一行有几个 0
int [] col0 = new int[m]; // 存储每一列有几个 0
Arrays.fill(row, 1); // 将存储乘积的两个数组的值都赋为 1,便于计算乘积
Arrays.fill(col, 1);
// 存储值
for (int i = 0;i < n;i++) {
for (int j = 0;j < m;j++) {
arr[i][j] = in.nextInt();
// 计算行列 0 的个数,为 0 时不算入乘积
if (arr[i][j] == 0) {
row0[i]++;
col0[j]++;
}else {
row[i]*= arr[i][j];
col[j]*= arr[i][j];
}
}
}
int ans = 0; // 存储最大值
for (int i = 0;i < n;i++) {
for (int j = 0;j < m;j++) {
// 计算行列中参与计算的值是否存在 0
int flag = row0[i] + col0[j];
if (arr[i][j] == 0) flag -= 2;
// 存在 0 时则整个乘积为 0 ,因此只要比较不存在 0 时的值就好
if (flag == 0) {
// 虽然 arr[i][j] 不参与计算,但它可能作为 0 出现在被除数中,因此分开计算
if (arr[i][j] == 0) {
ans = Math.max(ans, row[i]*col[j]);
}else {
ans = Math.max(ans, (row[i]/arr[i][j])*(col[j]/arr[i][j]));
}
}
}
}
// 输出结果
System.out.println(ans);
}
}
}
最后
如果有所收获请点赞支持一下作者哦,您的点赞是我持续创作的动力!!