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的更多相关文章

  1. Largest Submatrix(动态规划)

    Largest Submatrix Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

  2. POJ-3494 Largest Submatrix of All 1’s (单调栈)

    Largest Submatrix of All 1’s Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 8551   Ac ...

  3. hdu 2870 Largest Submatrix(平面直方图的最大面积 变形)

    Problem Description Now here is a matrix with letter 'a','b','c','w','x','y','z' and you can change ...

  4. 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 ...

  5. codeforces 407D Largest Submatrix 3

    codeforces 407D Largest Submatrix 3 题意 找出最大子矩阵,须满足矩阵内的元素互不相等. 题解 官方做法 http://codeforces.com/blog/ent ...

  6. 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 ...

  7. 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 ...

  8. 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 ...

  9. HDU 2870 Largest Submatrix (单调栈)

    http://acm.hdu.edu.cn/showproblem.php? pid=2870 Largest Submatrix Time Limit: 2000/1000 MS (Java/Oth ...

  10. MINSUB - Largest Submatrix

    MINSUB - Largest Submatrix no tags  You are given an matrix M (consisting of nonnegative integers) a ...

随机推荐

  1. 第8篇NFS PersistentVolume

    一.部署nfs服务端: k8s-master 节点上搭建了 NFS 服务器,也可以在部署节点搭建,原理一样 (1)安装nfs服务: yum install -y nfs-utils rpcbind v ...

  2. Es学习第三课, ElasticSearch基本的增删改查

    前面两课我们了解了ES的基本概念并且学会了安装ES,这节课我们就来讲讲ES基本的增删改查:ES主要对外界提供的是REST风格的API,我们通过客户端操作ES本质上就是API的调用.在第一课我们就讲了索 ...

  3. PHP发送公众号模板消息

    <?php /* * 模板消息发送,电脑端测试时需要手动填写openid * 微信端会自动获取当前openid发送无需填写 */ header("Content-type: text/ ...

  4. CSS 布局 - Overflow

    CSS 布局 - Overflow CSS overflow 属性用于控制内容溢出元素框时显示的方式. 这里的文本内容是可以滚动的,滚动条方向是垂直方向.dd马达价格 这里的文本内容是可以滚动的,滚动 ...

  5. hdu 6134 Battlestation Operational (莫比乌斯反演+埃式筛)

    Problem Description   > The Death Star, known officially as the DS-1 Orbital Battle Station, also ...

  6. nginx启动报错: libpcre.so.1/libpcre.so.0: cannot open shared object file

    1.用file /bin/ls查看当前系统是几位的 2.64位系统创建软连接:ln -s /usr/local/lib/libpcre.so.1 /lib64 3.32未系统创建软连接:ln -s / ...

  7. <Jmeter入门不放弃>之<2.常用功能>

    大家这里参考学习的时候,我就不在这里配截图了,因为需要你打开工具根据文档自己去找,才有印象,大家一定要启动JMeter!跟着理解操作 一.测试计划 用来描述一个性能测试,所有内容都是基于这个计划,这谁 ...

  8. 为什么要用getBaseContext()方法代替this?(转)

    问:this 常常引用当前的 context.但是有些时候,必须使用getBaseContext()来代替this.就是说使用this会引发错误. 如下面的例子: Spinner spinner = ...

  9. JS-layui:百科

    ylbtech-JS-layui:百科 layui,是一款采用自身模块规范编写的前端 UI 框架,遵循原生 HTML/CSS/JS 的书写与组织形式,门槛极低,拿来即用.其外在极简,却又不失饱满的内在 ...

  10. layui弹出层回调的使用

    <%@page language="java" contentType="text/html; charset=UTF-8"%> <%@ in ...