ICPC2019银川站 K题 Largest Common Submatrix 单调栈

题意:给两个N*M的矩阵,矩阵的元素为1~N*M的全排列,求两个矩阵的最大相同子矩形的面积(元素数)。N M范围1000

这种求最大相同子矩形的题目很容易想到使用单调栈来解决。首先给出的两个矩阵元素为均为排列,即每个数字都只出现一次。那么A矩阵的每个数字都在B矩阵有一个唯一的对应位置。我们可以先建立一个pos数组来求出A矩阵中每个数字在B矩阵中的对应位置。

那么对于A矩阵的每个数字,我们可以算出从这个数字往右一直可以匹配多少个B矩阵的数字,即对于每个数字,都往右匹配出一个尽量长的1*x的子矩阵。

现在对于每一列从上往下看,每个数字能匹配到最右边的端点已知,那么这就变成了经典的求柱形图最大面积的题目了。(POJ2559)

然后单调栈爆搞就完事啦~

但是要注意这种情况

A矩阵为

1 2 3

4 5 6

B矩阵为

2 3 1

4 5 6

对于A矩阵的2和5,最右匹配长度都为2,但是对应B矩阵的2 5的列位置却不相同,不能直接像求最大柱形图那样做。需要先判断合法,如果不合法的话,清空之前的栈,再从下一位置开始push。(具体看注释吧QAQ)

之前从来没写过单调栈的题(连POJ2559那个模板题都没写过),光知道有这么个东西,不过现场随便推了推就轻松过啦~

#include <bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
#define fi first 
#define se second
#define mp make_pair
#define rep(i,a,b) for(int i = a; i <= b; i++)
const int maxn = 1005;
const int maxnn = 1e6 + 5;
int mat1[maxn][maxn], mat2[maxn][maxn], len[maxn][maxn];
pii pos[maxnn];
stack<pii>s;//第一维是权值,第二维是长度 
int n, m;
int zuo(int pp){//非法时,清空单调栈并记录答案 
	int temp = 0;
	while(!s.empty()){
		temp = max((pp - s.top().se) * s.top().fi, temp);
		s.pop();
	}
	s.push(mp(0, 0));
	return temp;
}

int main(){
	cin >> n >> m;
	rep(i,1,n)	rep(j,1,m)	cin >> mat1[i][j];
	rep(i,1,n)	rep(j,1,m)	cin >> mat2[i][j], pos[mat2[i][j]] = mp(i, j);//求出每个点的对应位置 
	rep(i,1,n)	for(int j = 1; j <= m;){
			int k = 1;
			pii now = pos[mat1[i][j]];
			for(; j + k <= m && now.se + k <= m; k++)
				if(mat1[i][j+k] != mat2[now.fi][now.se+k])//每个点向右找到最远匹配长度 
					break;
			while(k)	len[i][j++] = k--;//用len记录每个点的匹配长度 
		}
	int ans = 0;
	s.push(mp(0, 0));
	rep(i,1,m){
		rep(j,1,n){
			if(j != 1 && (pos[mat1[j][i]].fi != pos[mat1[j-1][i]].fi+1 || pos[mat1[j][i]].se != pos[mat1[j-1][i]].se))//如果非法
				ans = max(ans, zuo(j));
			int now = j, tempnow = j;//now记录当前位置,用于计算答案。tempnow记录预插入位置 
			while(true){//单调栈的弹出操作,同时计算答案 
				if(len[j][i] > s.top().fi)	break;
				ans = max((now - s.top().se) * s.top().fi, ans);
				tempnow = s.top().se;
				s.pop();
			}
			s.push(mp(len[j][i], tempnow));//单调栈插入操作 
		}
		ans = max(ans, zuo(n+1));//本列判断结束,清空单调栈 
	}
	cout << ans << endl;
}

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值