传送门:https://nanti.jisuanke.com/t/42391
题目大意:给你两个 n * m 的二维排列(1 ~ n * m)。问你最大重合公共子矩阵。
题目思路:
这种问极大子矩阵的题目有两种方法求解:单调栈 / 悬线法
方法一:单调栈
1.关键:
求出每个点最远往上衍生多远的距离记为val.对于每一行,相当于求解以这一行为底边的最大柱状图面积。
2.思路:
对于每个点,正反跑一遍单调栈。求出往左最远能碰到的距离,往右最远能碰到的距离。(注意转移的时候左边界是如何衍生的,类似dp)
3.注意点:
这里需要注意连通性:单调栈中不仅每一列高度是递增的,而且要是在原矩阵中列也是两两相邻的。所以当新增一列时需判断连通性。若不连通,需要将栈清空再放入新的列.
方法二:悬线法(dp) -- 核心思想和 [方法一] 大同小异.
1.悬线:
对于每个点,他往上最多能走多少的线叫悬线。
2.原理:
极大矩阵的上边界一定被某个障碍物限制着(否则答案可以更大)。那么假设这个障碍物在第i列。那么第i列的极大矩阵的底边那个点到障碍物就是一条悬线.
3.1 答案求解
求解出每个点的1.悬线长,2.以该点为底,以悬线为高往左能够衍生多长,往右能衍生多长。答案面积就直接计算了。
①悬线长height数组:对于每个点(i,j),按(i - 1 , j)点转移即可。
②左右衍生长度left ,right数组:
第一遍:按(i - 1 , j)/(i , j - 1)点转移即可。
第二遍:对于那些height > 1 的点(i , j),它还需要考虑点(i - 1, j) 的 left 和 right 数组 对本行的限制。
3.2 过程要点
注意到:left,right数组再进行第一遍第二遍dp的时候概念是发生变化的。
第一遍时:left,right数组单纯代表每个点往左/右边最多能衍生多少
第二遍时:left,right数组代表以该点悬线为高往左/右边最多能够衍生多少
所以一定要先让所有点左右衍生到尽头,再考虑上一行对本行的限制.(也就是按顺序来)
因为根据dp的含义: left数组只受左边第一个障碍物,还有上一层的left的影响。
如果left[i][j - 1] 提前算完两步了。它的含义也变了。那么在left[i][j]转移的时候left[i][j - 1]将不能转移。
知乎讲解:https://zhuanlan.zhihu.com/p/46382722