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 ...
随机推荐
- ActiveMQ安装步骤及介绍
1.什么是ActiveMQ? ActiveMQ是一个完全支持JMS1.1和J2EE1.4规范的JMS Provider实现. JMS相关概念 提供者:实现JMS规范的消息中间件服务器 客户端:发送或接 ...
- string.format()详解
java中: 从 Java 5.0 开始,String 类新增了一个强大的字符串格式化方法 format().这个方法到现在用的人还是不多,实在是一种浪费.本文带你快速过一遍这个方法的功能,将来你要用 ...
- MySQL慢SQL语句常见诱因
原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/11429037.html 1. 无索引.索引失效导致慢查询 如果在一张几千万数据的表中以一个没有索引的列 ...
- 记一次pycharm和vscode因网络问题插件下载失败的问题
WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connec ...
- php经典趣味算法
1.一群猴子排成一圈,按1,2,…,n依次编号.然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数,再数到第m只,在把它踢出去…,如此不停的进行下去,直到最后只剩下一只猴子为止,那只猴子就叫 ...
- Cisco基础(六):配置目前网络环境、项目阶段练习
一.配置目前网络环境 目标: 一家新创建的IT公司,公司位于北京有80多台服务器 目前网络环境使用技术,通过端口映射技术将web服务器发布给Internet: 三层交换:汇聚接入层交换机 默认路由:实 ...
- K-th Closest Distance
题目链接 题意:n个数,q次查询,查询[l , r] 内, | a[i] - p | 第k大的数 思路:主席树维护下权值大小,二分答案,查询区间[p - mid, p + mid] 的个数 #incl ...
- 使用Canvas操作像素
现代浏览器支持通过<video>元素播放视频.大多数浏览器也可以通过MediaDevices.getUserMedia() API访问摄像头.但即使这两件事结合起来,我们也无法直接访问和操 ...
- react jsx 中使用 switch case 示例
<div> <span>适用平台:</span> <span>{(() => { switch (currentItems.usePlatform ...
- openlayers中单击获取要素
openlayers中单击获取要素 分类专栏: GIS 总结 OpenLayers 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接: ...