有趣的算法题之矩阵的特别乘积

矩阵元素相乘

        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);

具体步骤:

  1. 初始化两个数组 row 和 col,分别用于存储每一行和每一列(除了当前位置元素外)的乘积。同时,初始化两个数组 row0 和 col0,用于记录每一行和每一列中 0 的个数。

  2. 遍历矩阵 A ,填充 arr 数组,并同时更新 rowcolrow0 和 col0 数组。对于 arr[i][j],如果它是 0 ,则增加 row0[i] 和 col0[j] 的计数;否则,将其乘到 row[i] 和 col[j] 上。

  3. 遍历矩阵 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]

  4. 在计算过程中,不断更新最大值 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);
        }
    }
}

最后


如果有所收获请点赞支持一下作者哦,您的点赞是我持续创作的动力!! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值