题目
给你两个矩阵矩阵内的数字为N*M的排列。
找出两矩阵的子矩阵能相等的最大大小。
N,M<=1000
链接
题解思路
单调栈解法
我们可以维护两个矩阵中其中一个的每个数的列可以往左扩展的距离。(这一步应该比较容易想到)
然后问题就变成了对于每一列的数,求每个数能往左右不超过他的值扩展的距离最大值。
再乘以长度就是矩形的大小。
运用单调栈后置处理
单挑栈存小值。
并且存上这个点能往左边扩展的最大值stk[i].l。
定义一个临时变量l,用于记录这个点能往右边扩展的最大值。
l只需加上之前出栈的点+1即可。因为这个点必然小于之前出栈的点。
大概情况是这样。
随后入栈的这个点肯定是比这些点都小的,所以把之前累加的 l 赋值给它。
当无法往下延展时需要特判,以及让栈空。
这里体现出的后置思想让这部分理解有点绕。
还是题目血少了。
悬线法
悬线法介绍
将a数组映射成标准数组,然后b数组的每个值根据a数组的映射值对应改变。
然后就能用悬线法来操作了。(至于为什么能,这是悬线法的性质,可以看这篇博客里面的论文)
列之间差1,行之间差m。
套上悬线法的板子直接能过。
AC代码
单调栈解法
/*从你的全世界路过.*/
#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 200100;
int n , m ;
int a[2010][2010] ;
int b[2010][2010] ;
int f[2010][2010] ;
int ans = 0 ;
PII mp1[2010*2010] , mp2[2010*2010] ;
struct node
{
int l , h ,id ;
}stk[2010] ;
void work(int j )
{
for (int i = 0 ; i <= n+1 ; i++ )
stk[i] = {0,0,0} ;
int top = 0 ;
mp2[0].first = INF , mp2[0].second = INF ;
f[n+1][j] = 0 ;
int falg = 0 ;
for (int i = 1 ; i <= n + 1 ; i++ )
{
if ( mp1[b[i][j]].first == mp1[b[i-1][j]].first + 1 && mp1[b[i][j]].second == mp1[b[i-1][j]].second )
falg = 1 ;
else
falg = 0 ;
int l = 0 , len = 0 ;
while ( top && (stk[top].h > f[i][j] || !falg ) )
{
int s = (stk[top].l + 1 + l)*(stk[top].h);
l += stk[top].l + 1 ;
ans = max(s,ans) ;
top--;
}
stk[++top].h = f[i][j] ;
if ( falg )
stk[top].l = l ;
else
stk[top].l = 0 ;
}
}
void solve()
{
cin >> n >> m ;
for (int i = 1 ; i <= n ; i++ )
for (int j = 1 ; j <= m ; j++ )
cin >> a[i][j] , mp1[a[i][j]] = {i,j} ;
for (int i = 1 ; i <= n ; i++ )
for (int j = 1 ; j <= m ; j++ )
cin >> b[i][j] , mp2[b[i][j]] = {i,j} ;
for (int i = 1 ; i <= n ; i++ )
for (int j = 1 ; j <= m ; j++ )
{
if ( mp1[b[i][j]].first == mp1[b[i][j-1]].first && mp1[b[i][j]].second == mp1[b[i][j-1]].second + 1 )
{
f[i][j] = f[i][j-1] + 1 ;
}else
f[i][j] = 1 ;
}
for (int j = 1 ; j <= m ; j++ )
work(j) ;
cout << ans << "\n" ;
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
solve() ;
return 0 ;
}
悬线法
/*从你的全世界路过.*/
#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 200100;
int a[1010][1010] ;
int vis[1010*1010] ;
int l[2010][2010];
int r[2010][2010];
int up[2010][2010];
void solve()
{
int n , m ;
cin >> n >> m ;
for (int i = 1 ; i <= n*m ; i++ )
{
int t1 ;
cin >> t1 ;
vis[t1] = i;
}
for (int i = 1 ; i <= n ; i++ )
for (int j = 1 ; j <= m ; j++ )
{
int t1 ;
cin >> t1 ;
a[i][j] = vis[t1] ;
l[i][j] = r[i][j] = j ;
up[i][j] = 1 ;
}
for (int i = 1 ; i <= n ; i++ )
for (int j = 2 ; j <= m ; j++ )
if ( a[i][j] == a[i][j-1] + 1 )
l[i][j] = l[i][j-1] ;
for (int i = 1 ; i <= n ; i++ )
for (int j = m-1 ; j >= 1 ; j-- )
if ( a[i][j] + 1 == a[i][j+1] )
r[i][j] = r[i][j+1] ;
int ans = 0 ;
for (int i = 1 ; i <= n ; i++ )
for (int j = 1 ; j <= m ; j++ )
{
if ( i > 1 && a[i-1][j] + m == a[i][j] )
{
up[i][j] = up[i-1][j] + 1 ;
r[i][j] = min(r[i-1][j],r[i][j]);
l[i][j] = max(l[i-1][j],l[i][j]) ;
}
ans = max(ans,up[i][j]*(r[i][j]-l[i][j]+1)) ;
}
cout << ans << "\n" ;
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
solve() ;
return 0 ;
}