题意:给出由01组成的矩阵,求求全是1的次大子矩阵。

思路:

单调栈

全是1的最大子矩阵的变形,不能直接把所有的面积存起来然后排序取第二大的,因为次大子矩阵可能在最大子矩阵里面,比如:

1 0 0
1 1 1
1 1 1

有篇博主的代码细节处理的很好,由于矩阵每行的长度一致,则不必重复在数组末尾标记0;然后由于j是从1,最开始如果push进0的话,有两个好处:

  1.可以不受栈之前“残留”的元素m+1的影响

  2.不用再判断栈是某为空来确定wid的值

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 7; int n, m, ans;
string str[N];
int h[N], mx1, mx2;
stack <int> s; void solve(int x)
{
if(x > mx1)
mx2 = mx1, mx1 = x;
else if(x > mx2) mx2 = x;
}
int main()
{
scanf("%d%d",&n, &m);
for(int i = 1; i <= n; i++) {
cin >> str[i];
for(int j = 1; j <= m; j++) {
if(str[i][j-1] == '1') h[j] += 1;
else h[j] = 0;
}
s.push(0);
for(int j = 1; j <= m + 1; j++) {
while(h[j] < h[s.top()]) {
int index = s.top(); s.pop();
int x = j - 1 - s.top(), y = h[index];
solve(x * y);
solve((x - 1)* y);
solve(x * (y - 1));
}
s.push(j);
}
}
cout << mx2 << endl;
}

悬线法

通过悬线法,可以找到以点(i,j)为底的极大矩形。

u[i][j]、l[i][j]、r[i][j]分别表示以为底的极大矩形的上边界,左边界,右边界;

首先预处理:找到点(i,j)可以沿伸的的上端点、左端点,右端点 (dp)

For i = 1 to n
For j = 1 to m
u[i][j] = (i-1,j)==1 ? u[i-1][j] : i;
l[i][j] = (i,j-1)==1 ? l[i][j-1] : j;
For j = m to 1
r[i][j] = (i,j+1)==1 ? r[i][j+1] : j;

如图找到了(4,3)  的   上端点、左端点,右端点,但是这些边界并没有组成一个矩形,可以(4,3)的上端点为上边界,找到左右边界,这样就可以找到一个以点(4,3)为底、以点(4,3)上界为高的极大矩形。

For i = 1 to n
For j = 1 to m
if (i-1,j)==1
l[i][j] = max(l[i][j], l[i-1][j]
r[i][j] = min(r[i][j], r[i-1][j]
矩形面积就是 (r[i][j] − l[i][j] + 1) ∗ (i − u[i][j] + 1)
Code

#include<bits/stdc++.h>
using namespace std; const int N = 1e3+100; int n, m;
int g[N][N];
int u[N][N], l[N][N], r[N][N];
int hh, ll, rr, bb;
char str[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++){
scanf("%s",str+1);
for(int j = 1; j <= m; j++ ){
if(str[j] == '1') g[i][j] = 1;
else g[i][j] = 0;
}
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
u[i][j] = l[i][j] = r[i][j] = 0; for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(g[i][j] == 0) continue;
u[i][j] = g[i-1][j] == 1 ? u[i-1][j] : i;
l[i][j] = g[i][j-1] == 1 ? l[i][j-1] : j;
}
for(int j = m; j >= 1; j--){
if(g[i][j] == 0) continue;
r[i][j] = g[i][j+1] == 1 ? r[i][j+1] : j;
}
}
int ans = 0;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(g[i][j] == 0) continue;
if(g[i-1][j] == 1) {
l[i][j] = max(l[i][j], l[i-1][j]);
r[i][j] = min(r[i][j], r[i-1][j]);
}
if(ans<(r[i][j]-l[i][j]+1)*(i-u[i][j]+1)){
hh = u[i][j] , bb =i;
rr = r[i][j],ll=l[i][j];
ans = (r[i][j]-l[i][j]+1)*(i-u[i][j]+1);
}
}
}
int ans2 = max((rr-ll)*(bb-hh+1), (rr-ll +1)*(bb-hh));
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(g[i][j] == 0) continue;
if(hh==u[i][j]&&bb==i&&ll==l[i][j]&&rr==r[i][j])
continue;
if(ans2<(r[i][j]-l[i][j]+1)*(i-u[i][j]+1)){
ans2=(r[i][j]-l[i][j]+1)*(i-u[i][j]+1);
}
}
}
cout << ans2 << endl;
return 0;
}

悬线法的学习可以参考这篇博客(小声bb:虽然感觉现在没什么用,还不如单调栈)

https://blog.csdn.net/dbc_121/article/details/77503611

2019牛客暑期多校训练营(第二场) H-Second Large Rectangle(单调栈)的更多相关文章

  1. 牛客多校第二场H Second Large Rectangle 单调栈or悬线法

    Second Large Rectangle 题意 给出n*m的01矩阵,问由1组成的第二大的矩阵的大小是多少? 分析 单调栈(or 悬线法)入门题 单调栈 预处理出每一个点的最大高度,然后单调栈每一 ...

  2. 2019 牛客暑期多校 第二场 H Second Large Rectangle (单调栈)

    题目:https://ac.nowcoder.com/acm/contest/882/H 题意:一个大的01矩阵,然后现在要求第二大的全一矩阵是多少 思路:在这里我们首先学习一下另一个东西,怎么求直方 ...

  3. 2020牛客暑期多校训练营 第二场 K Keyboard Free 积分 期望 数学

    LINK:Keyboard Free 我要是会正经的做法 就有鬼了. 我的数学水平没那么高. 三个同心圆 三个动点 求围成三角形面积的期望. 不会告辞. 其实可以\(n^2\)枚举角度然后算出面积 近 ...

  4. 2020牛客暑期多校训练营 第二场 J Just Shuffle 置换 群论

    LINK:Just Shuffle 比较怂群论 因为没怎么学过 置换也是刚理解. 这道题是 已知一个置换\(A\)求一个置换P 两个置换的关键为\(P^k=A\) 且k是一个大质数. 做法是李指导教我 ...

  5. 2020牛客暑期多校训练营 第二场 I Interval 最大流 最小割 平面图对偶图转最短路

    LINK:Interval 赛时连题目都没看. 观察n的范围不大不小 而且建图明显 考虑跑最大流最小割. 图有点稠密dinic不太行. 一个常见的trick就是对偶图转最短路. 建图有点复杂 不过建完 ...

  6. 2020牛客暑期多校训练营 第二场 C Cover the Tree 构造 贪心

    LINK:Cover the Tree 最受挫的是这道题,以为很简单 当时什么都想不清楚. 先胡了一个树的直径乱搞的贪心 一直过不去.后来意识到这类似于最经典长链剖分优化贪心的做法 然后那个是求最大值 ...

  7. 2020牛客暑期多校训练营 第二场 B Boundary 计算几何 圆 已知三点求圆心

    LINK:Boundary 计算几何确实是弱项 因为好多东西都不太会求 没有到很精通的地步. 做法很多,先说官方题解 其实就是枚举一个点 P 然后可以发现 再枚举一个点 然后再判断有多少个点在圆上显然 ...

  8. 2020牛客暑期多校训练营 第二场 A All with Pairs 字符串hash KMP

    LINK:All with Pairs 那天下午打这个东西的时候状态极差 推这个东西都推了1个多小时 (比赛是中午考试的我很困 没睡觉直接开肝果然不爽 一开始看错匹配的位置了 以为是\(1-l\)和\ ...

  9. 2019牛客暑期多校训练营(第九场) D Knapsack Cryptosystem

    题目 题意: 给你n(最大36)个数,让你从这n个数里面找出来一些数,使这些数的和等于s(题目输入),用到的数输出1,没有用到的数输出0 例如:3  4 2 3 4 输出:0 0 1 题解: 认真想一 ...

随机推荐

  1. redhat_快捷键和shell命令操作.md

    tab键 命令自动补全 history !n 执行历史记录第n条 !! 执行上一条命令 !l 最后一条以l开头的命令 alias 创建命令的别名 alias 命令别名 = "命令行" ...

  2. .NET的并发编程(TPL编程)是什么?

    写在前面 优秀软件的一个关键特征就是具有并发性.过去的几十年,我们可以进行并发编程,但是难度很大.以前,并发性软件的编写.调试和维护都很难,这导致很多开发人员为图省事放弃了并发编程.新版 .NET 中 ...

  3. 剑指offer 查找和排序的基本操作:查找排序算法大集合

    重点 查找算法着重掌握:顺序查找.二分查找.哈希表查找.二叉排序树查找. 排序算法着重掌握:冒泡排序.插入排序.归并排序.快速排序. 顺序查找 算法说明 顺序查找适合于存储结构为顺序存储或链接存储的线 ...

  4. 【JS学习】for-in与for-of

    前言:本博客系列为学习后盾人js教程过程中的记录与产出,如果对你有帮助,欢迎关注,点赞,分享.不足之处也欢迎指正,作者会积极思考与改正. 总述: 名称 遍历 适用 for-in 索引 主要建议白能力对 ...

  5. Linux 使用命令行上传下载文件

    基本语法: 服务器: 用户名@ip:/路径 scp 要拷贝的文件 要存放的文件 上传文件到服务器 # 把本地 source.md 文件上传到 152.116.113.13 服务器的/home目录 # ...

  6. IDEA 常用的一些 (就几个) 快捷键

    快捷键 说明 Ctrl + P 提示类参数 Ctrl + Q 提示类的属性和方法包名 Ctrl + D 复制一行到下一行 Ctrl + F 查找 Ctrl + R 替换 Ctrl + Z 撤销 Ctr ...

  7. 通过trace分析优化其如何选择执行计划

    mysql5.6提供了对sql的跟踪trace,通过trace文件能够进一步了解为什么优化其选择执行计划a而不选b执行计划,帮助我们更好的理解优化其的行为. 使用方式:首先打开trace,设置格式为j ...

  8. gears-绕过rbash

    0x00 信息收集 0x01 smb攻击 crunch 生成密码的一个软件 @%%,这个是给的密码参数. crunch 4 4 -t @%%, -o words 最小4位,最长 4位 fcrackzi ...

  9. 【开源】我和 JAP(JA Plus) 的故事

    JA Plus 故事 程序员的故事如此简单之绕不过去的开源情结 我们准备做一件伟大的事,也可以说是一件真真正正普惠的事. 絮 是的,你没有看错,就是"絮"而非"序&quo ...

  10. 核酸检测:让我明白AQS原理

    春节越来越近了,疫情也越来越严重,但挡不住叫练携一家老小回老家(湖北)团聚的冲动.响应国家要求去我们做核酸检测了. 独占锁 早上叫练带着一家三口来到了南京市第一医院做核酸检测,护士小姐姐站在医院门口拦 ...