原题链接.
题目大意:
给定两个n*m的矩阵,求他们的最大公共子矩阵,并输出这个子矩阵中的元素个数。
所以输出4。
思路:
采用悬线法
悬线法用来求直方图中最大矩形的面积。在直方图的每个柱体顶端悬挂一根线,然后将线左右移动,并分别记录该移动的最左端和最右端。左右两端的长度再乘以高度,就是当前柱体可以形成的最大面积。
上图需要将两个矩阵预处理成一个矩阵,这个矩阵只存储第二个矩阵的数字在第一个矩阵中的位置。然后就可以通过判断b[i][j] - b[i-1][j]==m来确定上下是否相连。判断b[i][j]-b[i][j-1]==1来判断左右是否相连。抽象为了一个直方图问题。
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e3 + 5;
//a数组存储第一个矩阵,b存储映射矩阵,temp用来存储映射关系
int a[N][N], b[N][N],temp[N*N];
int n, m;
//c数组存储直方图的高度
int c[N],L[N],R[N],sum;
void solve() {
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (b[i][j] - b[i - 1][j] == m && i != 1) {
c[j] = c[j]+1;
}
else {
c[j] = 1;
}
L[j] = j;
R[j] = j;
}
for (int j = 1; j <= m; j++) {
//左端点
while (L[j] != 1 && c[j] <= c[L[j] - 1] && b[i][j] - b[i][L[j] - 1] == j - L[j] + 1) L[j] = L[L[j] - 1];
}
for (int j = m; j >0; j--) {
//右端点
while (R[j] != m && c[j] <= c[R[j] + 1] && b[i][j] - b[i][R[j] + 1] == j - R[j] - 1) R[j] = R[R[j] + 1];
}
for (int j = 1; j <= m; j++) {
sum = (R[j]-L[j]+1) * c[j];
ans = max(ans, sum);
}
}
cout << ans << endl;
}
int main() {
int k = 1;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
scanf("%d", &a[i][j]);
//记录出现的相对位置
temp[a[i][j]] = k++;
}
}
int x;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
scanf("%d", &x);
//提取两个矩阵的特征
b[i][j] = temp[x];
}
}
solve();
return 0;
}