Largest Submatrix 3
给出一个\(n\times m\)的网格图,第i行第j列上的格子有数字\(a[i][j]\),显然,你可以从中找到一个子矩阵,保证子矩阵中的数字互不相同,求子矩阵的最大面积,\(n,m\leq 400,a[i][j]\leq 400^2\)。
解
法一:确定上下边界+双指针
枚举子矩阵上边界和下边界,发现确定了左端点后,随着左端点的递增,右端点也在递增,于是可以双指针做到\(O(n^4)\)。
参考代码:
你来写啊
法二:确定上边界,枚举下边界,维护一个左端点对应的右端点
先讲做法,再讲正确性,看仔细了,实在看不懂来找我吧,感觉很难讲清楚。
从小到大枚举上边界\(u\),再从小到大枚举下边界\(d\),维护\(R_i\)表示上边界,下边界,以及第i列围成的矩形向右最远可以延伸的位置,维护set c[i]表示数字i在网格图中出现的列位置。
现在从\(d\rightarrow d+1\),先可以对于\(d+1\)行单独对于每个i求出其能最优延伸位置尝试对\(R_i\)更新,然后对于每个位置j,查询一个位置坐标p1小于等于j且与它相同的数字,查询一个位置p2大于等于j与之相同的数字,然后用\(p1\)更新\(R_j\),p2更新\(R_{p1}\),再把这一行的数字对应加入平衡树即可。
首先对于不同种类的数字,贡献是可以单独看的,这是一种很常见但我总是不记得的研究思路(如网格图中的行列独立,经典的错排问题),对于其中任意两个数字设其列位置为\(x_1,x_2(x_1\leq x_2)\),显然它可以利用\(x_2\)更新的\(R_i,i\)的范围为\([1,x_1]\).
此时如果我们另\(R_{x_1}=x_2\),日后倒序枚举i,进行操作\(R_i=min(R_i,R_{i+1})\),等价于\([1,x_1]\)中所有\(R_i\)都与\(x_2\)取min(区间修改转化为两点修改的思想,我又忘了)。
如果我们枚举对于一种数字每一对\((x_1,x_2)\)进行上段的操作,肯定可以得到这个数字的贡献,更进一步,这对数字只要枚举最靠近的一对。
于是我们需要平衡树来维护位置关系,现在下边界从\(d\)到了\(d+1\),考虑答案的来源,显然是因为新来的一行的数字,显然答案只涉及该行的数字种类的影响,于是考虑该行上的每种数字,一个数字i对答案的贡献,显然是来自两个方向,第一个是\(d\)行及以上的离他最近的数字和\(d+1\)行离它最近的数字。
于是我们可以利用法一的双指针暴力求出一行中每个数字离他最近的相同的数字,然后其它的,直接在平衡树中查找即可,当然你也可以先全部加进平衡树,再一个一个找,按照前面提到的方法更新即可。
时间复杂度有点不稳,\(O(n^3log(n))\),如果你看懂了,证明我语文有进步了。
参考代码:
#include <iostream>
#include <cstdio>
#include <set>
#include <cstring>
#define il inline
#define ri register
#define Size 450
#define intmax 0x3f3f3f3f
using namespace std;
set<int>c[Size*Size];
set<int>::iterator i1,i2;
int a[Size][Size],bu[Size*Size],R[Size];
il void read(int&);
int main(){
int n,m,ans(-intmax);
read(n),read(m);
for(int i(1),j;i<=n;++i)
for(j=1;j<=m;++j)
read(a[i][j]);
for(int u(1),d;u<=n;++u){
memset(R,0,sizeof(R));
for(int i(1);i<=16000;++i)c[i].clear();
for(int i(1);i<=m;++i)R[i]=m;
for(d=u;d<=n;++d){
for(int i(1),j(0);i<=m;++i){
while(!bu[a[d][j+1]]&&j<m)
++j,++bu[a[d][j]];
R[i]=min(R[i],j),--bu[a[d][i]];
i2=c[a[d][i]].upper_bound(i);
i1=c[a[d][i]].lower_bound(i);
if(i2!=c[a[d][i]].begin())
--i2,R[*i2]=min(R[*i2],i-1);
if(i1!=c[a[d][i]].end())R[i]=min(R[i],*i1-1);
}for(int i(m-1);i;--i)R[i]=min(R[i],R[i+1]);
for(int i(1);i<=m;++i)
ans=max(ans,(R[i]-i+1)*(d-u+1)),
c[a[d][i]].insert(i);
}
}printf("%d",ans);
return 0;
}
il void read(int &x){
x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}
法三:
法二的时间复杂度瓶颈在查询一个数字最靠近它的相同数字,然后出题人用了我最不记得的逆向思维,倒着枚举某个东西,看某个东西可以降时间复杂度。
我们倒序枚举\(u\),设\(l[i][j]\)为第i行第j个位置上的数字从左边最靠近它的在第\(i\)行到第\(u\)行那个数字,\(r[i][j]\)差不多意思,换了一个左右方向,显然当u减少的时候,\(l[i][j],r[i][j]\)的答案来源于第\(u-1\)行的最靠近第j列的两个相同数字,显然两次扫描,这个就可以\(O(n)\)维护(不晓得看代码)。
然后从\(u\)开始顺序枚举\(d\),然后我们是否就可以像法二一样求出每个\(u\sim d\)的\(R_i\)?接着我们的时间复杂度就降为了\(O(n^3)\)。
参考代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
#define Size 450
using namespace std;
int a[Size][Size],l[Size][Size],
r[Size][Size],R[Size],p[Size*Size];
il void read(int&);
int main(){
int n,m,ans(0);read(n),read(m);
for(int i(1),j;i<=n;++i)
for(j=1;j<=m;++j)
read(a[i][j]);
for(int u(n),d;u;--u){
memset(p,0,sizeof(p));
for(int i(1);i<=m;++i){
l[u][i]=p[a[u][i]],p[a[u][i]]=i;
for(d=u+1;d<=n;++d)
l[d][i]=max(l[d][i],p[a[d][i]]);
}for(int i(1);i<=160000;++i)p[i]=m+1;
for(int i(m);i;--i){
r[u][i]=p[a[u][i]],p[a[u][i]]=i,R[i]=m+1;
for(d=u+1;d<=n;++d)
r[d][i]=min(r[d][i],p[a[d][i]]);
}for(d=u;d<=n;++d){
for(int i(1);i<=m;++i){
R[i]=min(R[i],r[d][i]);
R[l[d][i]]=min(R[l[d][i]],i);
}for(int i(m-1);i;--i)
R[i]=min(R[i],R[i+1]);
for(int i(1);i<=m;++i)
ans=max(ans,(d-u+1)*(R[i]-i));
}
}printf("%d",ans);
return 0;
}
il void read(int &x){
x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}
法四:
子矩阵问题可以区间dp,算是长眼界了,设\(f[i][l][r]\)表示下边界为第i行,从第l列到第r列,最小的上边界,设\(b[i][j][k]\),表示第i行第j列往上出现数字k的最行坐标,然后就有
\(f[i][l][r]=min(f[i-1][l][r],f[i][l+1][r],f[i][l][r-1],b[i][l][a[i][r]],b[i][r][a[i][l]])\)
这个方程需要解释,

注意到\(min(f[i][l+1][r],f[i][l][r-1])\)保证了答案区域\(a,d\neq b,e\neq c,f\),但不能保证区域\(a,d\neq c,f\),再与\(f[i-1][l][r]\)取min,对于d来说,已经满足了\(a,b,e\neq d\),那么只要看\(c,f\)和\(d\)考虑在一起的上界了,因为对称,\(f\)也只要考虑\(a,d\)。
最后,显然如果你开这么多维度,肯定会\(MLE\),但实际上,f的第一维可以压掉,包括b的第一维,最后时间复杂度是\(O(n^3)\),虽然常数大了一点,空间有点大,但不失为一种奇妙的思路。
参考代码:
#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define Size 402
using namespace std;
int a[Size][Size],dp[Size][Size],
b[Size][Size*Size];
il void read(int&);
int main(){
int n,m,ans(0);read(n),read(m);
for(int i(1),j;i<=n;++i)
for(j=1;j<=m;++j)read(a[i][j]);
for(int i(1),j,k;i<=n;++i){
for(j=1;j<=m;++j)dp[j][j]=max(dp[j][j],b[j][a[i][j]]),b[j][a[i][j]]=i;
for(j=1;j<=m;++j)
for(k=j-1;k;--k)
dp[k][j]=max(dp[k][j],max(max(dp[k+1][j],dp[k][j-1]),max(b[j][a[i][k]],b[k][a[i][j]])));
for(k=1;k<=m;++k)
for(j=k;j<=m;++j)
ans=max(ans,(i-dp[k][j])*(j-k+1));
}printf("%d",ans);
return 0;
}
il void read(int &x){
x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}
Largest Submatrix 3的更多相关文章
- Largest Submatrix(动态规划)
Largest Submatrix Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...
- POJ-3494 Largest Submatrix of All 1’s (单调栈)
Largest Submatrix of All 1’s Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 8551 Ac ...
- hdu 2870 Largest Submatrix(平面直方图的最大面积 变形)
Problem Description Now here is a matrix with letter 'a','b','c','w','x','y','z' and you can change ...
- Largest Submatrix of All 1’s
Given a m-by-n (0,1)-matrix, of all its submatrices of all 1’s which is the largest? By largest we m ...
- codeforces 407D Largest Submatrix 3
codeforces 407D Largest Submatrix 3 题意 找出最大子矩阵,须满足矩阵内的元素互不相等. 题解 官方做法 http://codeforces.com/blog/ent ...
- Largest Submatrix of All 1’s(思维+单调栈)
Given a m-by-n (0,1)-matrix, of all its submatrices of all 1's which is the largest? By largest we m ...
- POJ 3494 Largest Submatrix of All 1’s 单调队列||单调栈
POJ 3494 Largest Submatrix of All 1’s Description Given a m-by-n (0,1)-matrix, of all its submatrice ...
- POJ - 3494 Largest Submatrix of All 1’s 单调栈求最大子矩阵
Largest Submatrix of All 1’s Given a m-by-n (0,1)-matrix, of all its submatrices of all 1’s which is ...
- HDU 2870 Largest Submatrix (单调栈)
http://acm.hdu.edu.cn/showproblem.php? pid=2870 Largest Submatrix Time Limit: 2000/1000 MS (Java/Oth ...
- MINSUB - Largest Submatrix
MINSUB - Largest Submatrix no tags You are given an matrix M (consisting of nonnegative integers) a ...
随机推荐
- 有趣的taskset命令,使进程再指定CPU上运行
前言 taskset命令,用于进程的CPU调优,可以把某进程,指定再某CPU内工作. 如还不明白,可以参考此文 http://www.361way.com/linux-context-switch/5 ...
- java两个数组内存图
- 开发react 应用最好用的脚手架 create-react-app
安装 npx create-react-app my-app cd my-app npm start 安装后之后,就是这样的了 配置 这样的”零配置”没法满足我们的需求,我们需要自定义,需要加一些 l ...
- Freemarker模板和依赖
<html> <head> <meta charset="utf-8"> <title>Freemarker入门小DEMO < ...
- Linux进程管理之ps的使用
主题Linux进程管理之ps工具的使用 一ps工具的介绍 ps: process state 进程状态ps - report a snapshot of the current processesL ...
- 大碗宽面Alpha第九周会议总结
软件工程每周博客: 本周二我们进行了小组会议,对正在做的评课网站——海大优选进行了整体分析和明确分工.首先我们对整体网页进行了需求分析和框架分析,然后进行了分工,前端同学两人,后端同学两人,文档同学一 ...
- Nginx功能模块汇总
主要文档 Nginx功能概述.为什么选择Nginx.Nginx安装.常见问题(FAQ).配置符号参考.调试 nginx.优化 Nginx.运行和控制Nginx 核心模块 Nginx事件模块.Nginx ...
- Dubbo入门到精通学习笔记(十八):使用Redis3.0集群实现Tomcat集群的Session共享
文章目录 1.单节点访问http://192.168.1.61:8082/pay-web-boss/: 2.增加多一个消费者节点:192.168.1.62,以同样的方式部署pay-web-boss工程 ...
- 28. Python编写自动化测试用例
接口文档已经提供了,requests库.unittest单元测试框架也已经介绍过,笔者相信读者朋友已经可以独立编写接口自动化测试用例了.但是有一些细节,我们需要聊一下.比如我们写登录接口测试用例,用户 ...
- InnoDB B树 锁
InnoDB B树 叶子=>主键+数记录非叶子=>主键1+主键3...主键4 事务和行锁 索引项加锁 相等条件来访问更新数据,避免使用范围条件 (1)InnoDB的行销是基于索引实现的,如 ...