【算法设计与分析】最大子矩阵问题 【Java实现】【深度优先搜索】【二分查找】【蓝桥杯】

最大子矩阵 【Java实现】【深度优先搜索】【二分查找】

问题描述:

小明有一个大小为N × M 的矩阵,可以理解为一个 N 行 M 列的二维数组。我们定义一个矩阵 m的稳定度 f(m) 为 f(m) = max(m) − min(m),其中max(m)表示矩阵 m中的最大值,min(m) 表示矩阵 m 中的最小值。现在小明想要从这个矩阵中找到一个稳定度不大于limit 的子矩阵,同时他还希望这个子矩阵的面积越大越好(面积可以理解为矩阵中元素个数)。
子矩阵定义如下:从原矩阵中选择一组连续的行和一组连续的列,这些行列交点上的元素组成的矩阵即为一个子矩阵。

解题算法思路

  • 假设一个矩阵的左上端点的坐标为(x1,y1),右下坐标的端点为(x2,y2)。我们可以暴力枚举两点的横坐标
    x1和x2,这样做的复杂度为(n^2)。
  • 接下来我们只需要找到y1和y2使得矩阵的稳定度不大于limit就可以了。如果同样暴力枚举的话复杂度是O(m^2)不可接受。所以考虑使用二分法优化。
  • 在下面的代码中,二分法通过check方法实现,当二分的值为mid时,也就是需要去判断是否存在宽度为mid的矩阵符合要求,因为我们前面预处理已经将高度压缩为1,所以这其实就是一个一维数组滑动窗口求最值问题,属于是单调队列的模板使用,求出每个长度为mid的窗口的最大值max和最小值min,只要有一个窗口符合max−min<=limit我们都返回true,否则返回false。
  • 接下来我们思考如何高效的求出一个矩阵的max和min,如果是一个一维数组,求区间的最值的方法就很多了,所以我们将二维矩阵预处理成一维。我们生成两个数组max[k][i][j]以及min[k][i][j],其中max[k][i][j]代表的含义是在第k列中,第i
    个元素到第j 个的元素最大值是多少,min数组同理。我们预处理的转移式子应该为:

max[k][i][j]=max(max[k][i][j−1],max[k][j][j])
这个式子的含义也很简单,对于区间 [i,j]的最大值应该是 [i,j−1]的最大值和 j 位值取较大值。由于我们处理的是每一列,这样时间复杂度是 O(n^2),由于总共有 m,所以总时间复杂度应该是 O(n^2m)。

接下来是源代码

1.import java.io.*;  
2.import java.util.ArrayDeque;  
3.import java.util.Deque;  
4.  
5.public class MaximumSubmatrix {//最大子矩阵  深度优先遍历   二分查找优化  
6.    //max[k][i][j]表示第k列中[i,j]之间的最大值  
7.    static int[][][] max;  
8.    static int[][][] min;  
9.    static int n, m, limit,ans;  
10.    static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));  
11.    static PrintWriter out=new PrintWriter(new OutputStreamWriter(System.out));  
12.    public static void main() throws IOException {  
13.        String[] s=br.readLine().split(" ");  
14.        n = Integer.parseInt(s[0]);  
15.        m = Integer.parseInt(s[1]);  
16.        max=new int[m+1][n+1][n+1];  
17.        min=new int[m+1][n+1][n+1];  
18.        for (int i = 1; i <= n; i++) {  
19.            s=br.readLine().split(" ");  
20.            for (int j = 1; j <= m; j++) {  
21.                max[j][i][i] = min[j][i][i] = Integer.parseInt(s[j-1]);  
22.            }  
23.        }  
24.        limit = Integer.parseInt(br.readLine());  
25.        //预处理  复杂度 n^2*m  
26.        for (int k = 1; k <= m; ++k) {  
27.            for (int i = 1; i <= n; ++i) {  
28.                for (int j = i + 1; j <= n; ++j) {  
29.                    max[k][i][j] = Math.max(max[k][i][j - 1], max[k][j][j]);  
30.                    min[k][i][j] = Math.min(min[k][i][j - 1], min[k][j][j]);  
31.                }  
32.            }  
33.        }  
34.        for (int x1 = 1; x1 <= n; x1++) {  
35.            for (int x2 = x1; x2 <= n; x2++) {  
36.                int l = 1, r = m;  
37.                while (l < r) {  
38.                    int mid = l + r + 1 >> 1;  
39.                    if (check(x1, x2, mid)) l = mid;  
40.                    else r = mid - 1;  
41.                }  
42.                if (check(x1,x2,r)) ans=Math.max(ans,(x2-x1+1)*r);  
43.            }  
44.        }  
45.        out.println("子矩阵的最大面积为  "+ans);  
46.        out.flush();  
47.        UI.main();  
48.    }  
49.  
50.    //k是窗口大小  
51.    static boolean check(int x1, int x2, int k) {  
52.        Deque<Integer> qmax = new ArrayDeque<>();  
53.        Deque<Integer> qmin = new ArrayDeque<>();  
54.        for (int i = 1; i <= m; i++) {  
55.            //处理最小  
56.            if (!qmin.isEmpty() && qmin.peekFirst() < i - k + 1) qmin.pollFirst();  
57.            while (!qmin.isEmpty() && min[qmin.peekLast()][x1][x2] > min[i][x1][x2]) qmin.pollLast();  
58.            qmin.offerLast(i);  
59.            //处理最大  
60.            if (!qmax.isEmpty() && qmax.peekFirst() < i - k + 1) qmax.pollFirst();  
61.            while (!qmax.isEmpty() && max[qmax.peekLast()][x1][x2] < max[i][x1][x2]) qmax.pollLast();  
62.            qmax.offerLast(i);  
63.            //说明窗口为k  
64.            if (i >= k && max[qmax.peekFirst()][x1][x2] - min[qmin.peekFirst()][x1][x2] <= limit) return true;  
65.        }  
66.        return false;  
67.    }  
68.}  

运行样例截图

在这里插入图片描述

算法效率分析

  • 一般来说,二分是个很有用的优化途径,因为这样会直接导致减半运算,而对于能否二分,有一个界定标准:状态的决策过程或者序列是否满足单调性或者可以局部舍弃性。对于这道题而言,因为本质上横坐标x确定的是矩阵高,而y确定的是矩阵的宽,所以我们去二分宽度——查找矩阵内是否存在宽度为L的矩阵稳定度不大于limit。如果存在一个宽度为L的矩阵符合要求,那么一定能找到一个宽度在区间 [L,L−1]的矩阵也符合要求。所以这道题可以使用二分。
  • 当二分得到最长宽度为r 时,该矩阵的面积就为(x2−x1+1)∗r,每次用一个全局变量更新答案。考虑时间复杂度——枚举横坐标为 O(n^2),
  • 二分的复杂度为O(logm),每次check判定的复杂度是O(m),所以整体时间复杂度为O(n^2mlogm)。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值