子矩阵

子矩阵

题目描述

给定一个 n × m (n 行 m 列)的矩阵。设一个矩阵的价值为其所有数中的最大值和最小值的乘积。求给定矩阵的所有大小为 a × b (a 行 b 列)的子矩阵的价值的和。答案可能很大,你只需要输出答案对 998244353 取模后的结果。

输入格式

输入的第一行包含四个整数分别表示 n, m, a, b ,相邻整数之间使用一个空格分隔。

接下来 n 行每行包含 m 个整数,相邻整数之间使用一个空格分隔,表示矩阵中的每个数 Ai, j 。

输出格式

输出一行包含一个整数表示答案。

样例输入

2 3 1 2
1 2 3
4 5 6

样例输出

58

解题思路

采用滑块方法进行计算,参考来源:蓝桥杯笔记-2023年第十四届省赛真题-子矩阵

例如:下面说这个矩阵

3 5 1 6 1
1 2 4 7 6
9 4 8 4 6
1 3 7 1 3
4 5 8 9 2

要求其3X3的所有子矩阵的极值乘积和,先求一行b列(1*3)的子矩阵极值乘积和,求出两个列表:

最大值maxL

5 6 6
4 7 7
9 8 8
7 7 7
8 9 9

最小值minL

1 1 1
1 2 4
4 4 4
1 1 1
4 5 2

将矩阵旋转

最大值maxL

5 4 9 7 8
5 7 8 7 9
6 7 8 7 9

最小值minL

1 1 4 1 4
1 2 4 1 5
1 4 4 1 2

根据上面的方法,再次求每行的滑块的最大值和最小值

子矩阵最大值表

9 9 9
8 8 9
8 8 9

子矩阵最小值表

1 1 1
1 1 1
1 1 1

最后,将两表一一相乘并求和,得到子矩阵的价值的和。

代码

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    static final long MOD=998244353;
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int a = scanner.nextInt();
        int b = scanner.nextInt();

        int[][] matrix = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                matrix[i][j] = scanner.nextInt();
            }
        }
        long num=result(matrix,n,m,a,b);
        System.out.println(num);
        scanner.close();
    }

    //获得两个最大和最小的列表数组
    public static long result(int[][] matrix,int n,int m,int a,int b) {
        int col_num=m-b+1;
        int row_num=n-a+1;
        int[][] matrix_max_copy=new int[n][col_num];
        int[][] matrix_min_copy=new int[n][col_num];

        //找到最大值的表
        for(int i=0;i<n;i++){
            matrix_max_copy[i]=find_Max(matrix[i],b);
        }
        int[][] matrix_max= transpose(matrix_max_copy);
        for(int i=0;i<col_num;i++){
            matrix_max[i]=find_Max(matrix_max[i],a);
        }
        //找到最小值

        for(int i=0;i<n;i++){
            matrix_min_copy[i]=find_Min(matrix[i],b);
        }
        int[][] matrix_min=transpose(matrix_min_copy);
        for(int i=0;i<col_num;i++){
            matrix_min[i]=find_Min(matrix_min[i],a);
        }
        long sum=0,temp=0;
        for(int i=0;i<col_num;i++){
            for(int j=0;j<row_num;j++){
                temp=(matrix_min[i][j]%MOD)*(matrix_max[i][j]%MOD)%MOD;
                sum=(sum%MOD+temp)%MOD;
            }
        }
        return sum;
    }

    //矩阵转置
    public static int[][] transpose(int[][] matrix) {
        if (matrix == null || matrix.length == 0) return new int[0][0];
        int rows = matrix.length;
        int cols = matrix[0].length;
        int[][] result = new int[cols][rows];

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                result[j][i] = matrix[i][j];
            }
        }
        return result;
    }

    //返回滑动窗口的最小值列表,以数组的形式返回
    public static  int[] find_Min(int[] nums,int len) {
        List<Integer> windows_Min = new ArrayList<>();
        List<Integer> windows=new ArrayList<>();
        for (int i = 0; i < nums.length; i++){
            if(windows.size()<len){
                windows.add(nums[i]);
                if(windows.size()==len){
                    int min=Min_number(windows);
                    windows_Min.add(min);
                }
            }else{
                //删除队列头元素
                windows.remove(0);
                windows.add(nums[i]);
                int min=Min_number(windows);
                windows_Min.add(min);
            }
        }
        int[] res=new int[windows_Min.size()];
        for(int i=0;i<windows_Min.size();i++){
            res[i]=windows_Min.get(i);
        }
        return res;
    }

    //返回滑动窗口的最大值列表,以数组的形式返回
    public static  int[] find_Max(int[] nums,int len) {
        List<Integer> windows_Max = new ArrayList<>();
        List<Integer> windows=new ArrayList<>();
        for (int i = 0; i < nums.length; i++){
            if(windows.size()<len){
                windows.add(nums[i]);
                if(windows.size()==len){
                    int max=Max_number(windows);
                    windows_Max.add(max);
                }
            }else{
                //删除队列头元素
                windows.remove(0);
                windows.add(nums[i]);
                int max=Max_number(windows);
                windows_Max.add(max);
            }
        }
        int[] res=new int[windows_Max.size()];
        for(int i=0;i<windows_Max.size();i++){
            res[i]=windows_Max.get(i);
        }
        return res;
    }

    //寻找列表最大值
    public static int Max_number(List<Integer> nums){
        Integer Max=Integer.MIN_VALUE;
        for (Integer num : nums) {
            if (Max < num) Max = num;
        }
        return Max;
    }

    //寻找列表最小值
    public static int Min_number(List<Integer> nums){
        Integer Min=Integer.MAX_VALUE;
        for (Integer num : nums) {
            if (Min > num) Min = num;
        }
        return Min;
    }
}
posted @ 2025-04-11 12:08  狐狸胡兔  阅读(30)  评论(0)    收藏  举报