文章目录

  • 悬线法
    • 学习资料
    • 题目
      • [ZJOI2007]棋盘制作
      • luogu4147 玉蟾宫
  • 枚举所有极大子矩形
    • 学习资料
    • 题目
      • 奶牛浴场
  • 单调栈
    • 学习资料

悬线法

学习资料

  1. 题解 P1169 【[ZJOI2007]棋盘制作By 顾z
  2. 最大子矩阵问题&悬线法 学习笔记By Clove_unique
  3. [DP专题]悬线法 ByLevenKoko
  4. 浅谈用极大化思想解决最大子矩形问题By 王知昆

一些说明:这个算法比较套路

悬线:上端点覆盖了一个障碍点或达到整个矩形上端的有效竖线。

如果将一个悬线向左右两个方向尽可能移动所得到的有效子矩形称为这个悬线所对应的子矩形,那么所有悬线所对应的有效子矩形的集合一定包含了所有极大子矩形的集合。

题目

[ZJOI2007]棋盘制作

【题目描述】
国际象棋是世界上最古老的博弈游戏之一,和中国的围棋、象棋以及日本的将棋同享盛名。据说国际象棋起源于易经的思想,棋盘是一个8∗88 * 88∗8大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳。而我们的主人公小Q,正是国际象棋的狂热爱好者。作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好朋友小W决定将棋盘扩大以适应他们的新规则。小Q找到了一张由N∗MN * MN∗M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种颜色之一。小Q想在这种纸中裁减一部分作为新棋盘,当然,他希望这个棋盘尽可能的大。不过小Q还没有决定是找一个正方形的棋盘还是一个矩形的棋盘(当然,不管哪种,棋盘必须都黑白相间,即相邻的格子不同色),所以他希望可以找到最大的正方形棋盘面积和最大的矩形棋盘面积,从而决定哪个更好一些。于是小Q找到了即将参加全国信息学竞赛的你,你能帮助他么?

【输入文件】

第一行包含两个整数N和M,分别表示矩形纸片的长和宽。接下来的N行包含一个N * M的01矩阵,表示这张矩形纸片的颜色(0表示白色,1表示黑色)。

【输出文件】

包含两行,每行包含一个整数。第一行为可以找到的最大正方形棋盘的面积,第二行为可以找到的最大矩形棋盘的面积(注意正方形和矩形是可以相交或者包含的)。

【输入样例】

3 3
1 0 1
0 1 0
1 0 0

【输出样例】

4
6
【数据规模】

对于20%的数据,N, M ≤ 80
对于40%的数据,N, M ≤ 400
对于100%的数据,N, M ≤ 2000

分析:
悬线法:
left[i][j]left[i][j]left[i][j]表示从(i,j)(i,j)(i,j)所能走到的最左边的位置
right[i][j]right[i][j]right[i][j]表示从(i,j)(i,j)(i,j)所能走到的最右边的位置
over[i][j]over[i][j]over[i][j]表示从(i,j)(i,j)(i,j)所能走到的最上边的长度
这个方法挺套路的。这样求出的面积中一定包含最优解

注意事项:
(1)调试过后或者修改之后必要时记得还原
(2)注意处理边界,谨慎
(3)看清有些变量的值不一定相等

代码

/***********************
User:Mandy.H.Y
Language:c++
Problem:luogu1169
Algorithm:悬线法
Date:2019.8.13
***********************/#include<bits/stdc++.h>
#define Max(x,y) ((x) > (y) ? (x) : (y))
#define Min(x,y) ((x) < (y) ? (x) : (y))using namespace std;const int maxn = 2005;int n,m,ans1,ans2;
int a[maxn][maxn];
int lef[maxn][maxn],rit[maxn][maxn],ovr[maxn][maxn]; template<class T>inline void read(T &x){x = 0;bool flag = 0;char ch = getchar();while(!isdigit(ch)) flag |= ch == '-',ch = getchar();while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();if(flag) x = -x;
}template<class T>void putch(const T x){if(x > 9) putch(x / 10);putchar(x % 10 | 48);
}template<class T>void put(const T x){if(x < 0) putchar('-'),putch(-x);else putch(x);
}void file(){freopen("chess.in","r",stdin);freopen("chess.out","w",stdout);
}void readdata(){read(n);read(m);for(int i = 1;i <= n; ++ i)for(int j = 1;j <= m; ++ j){read(a[i][j]);lef[i][j] = rit[i][j] = j;ovr[i][j] = 1;}}void work(){for(int i = 1;i <= n; ++ i)for(int j = 2;j <= m; ++ j)if(a[i][j] != a[i][j - 1])lef[i][j] = lef[i][j - 1]; for(int i = 1;i <= n; ++ i)for(int j = m - 1;j >= 1; -- j)if(a[i][j] != a[i][j + 1])rit[i][j] = rit[i][j + 1];for(int i = 1;i <= n; ++ i)//调试过后或者修改之后必要时记得还原for(int j = 1;j <= m; ++ j){if(i > 1 && a[i][j] != a[i - 1][j]){//大于1时就更新,等于1(上边界)直接算 lef[i][j] = max(lef[i][j],lef[i - 1][j]);rit[i][j] = min(rit[i][j],rit[i - 1][j]);ovr[i][j] = ovr[i - 1][j] + 1;}int len1 = rit[i][j] - lef[i][j] + 1;int len2 = min(ovr[i][j],len1);ans1 = max(ans1,len2 * len2);ans2 = max(ans2,len1 * ovr[i][j]);//ovr[i][j]不一定等于len2 }put(ans1);puts("");put(ans2);
}int main(){//  file();readdata();work();return 0;
}
luogu4147 玉蟾宫

题目传送门:luogu4147

题目背景
有一天,小猫rainbow和freda来到了湘西张家界的天门山玉蟾宫,玉蟾宫宫主蓝兔盛情地款待了它们,并赐予它们一片土地。

题目描述
这片土地被分成N*M个格子,每个格子里写着’R’或者’F’,R代表这块土地被赐予了rainbow,F代表这块土地被赐予了freda。

现在freda要在这里卖萌。。。它要找一块矩形土地,要求这片土地都标着’F’并且面积最大。

但是rainbow和freda的OI水平都弱爆了,找不出这块土地,而蓝兔也想看freda卖萌(她显然是不会编程的……),所以它们决定,如果你找到的土地面积为S,它们每人给你S两银子。

输入格式
第一行两个整数N,M,表示矩形土地有N行M列。

接下来N行,每行M个用空格隔开的字符’F’或’R’,描述了矩形土地。

输出格式
输出一个整数,表示你能得到多少银子,即(3*最大’F’矩形土地面积)的值。

输入输出样例
输入 #1 复制
5 6
R F F F F F
F F F F F F
R R R F F F
F F F F F F
F F F F F F
输出 #1 复制
45
说明/提示
对于50%的数据,1<=N,M<=200

对于100%的数据,1<=N,M<=1000

分析:
模板题。
注意事项:
(1)注意看清题目要求
(2)right右边最远位置要倒序,+1递推
(3)注意边界

代码:

/**********************
User:Mandy.H.Y
Language:
Problem:
Algorithm:
**********************/#include<bits/stdc++.h>using namespace std;const int maxn = 1005;int n,m,ans = 0;
char a[maxn][maxn];
int _left[maxn][maxn];
int _right[maxn][maxn];
int _over[maxn][maxn];template<class T>inline void read(T &x){x = 0;bool flag = 0;char ch = getchar();while(!isdigit(ch)) flag |= ch == '-',ch = getchar();while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();if(flag) x = -x;
}template<class T>void putch(const T x){if(x > 9) putch(x / 10);putchar(x % 10 | 48);
}template<class T>void put(const T x){if(x < 0) putchar('-'),putch(-x);else putch(x);
}void file(){freopen("testdata(2).in","r",stdin);
//  freopen("palace.out","w",stdout);
}void readdata(){read(n);read(m);for(int i = 1;i <= n; ++ i){for(int j = 1;j <= m; ++ j){char c = getchar();while(c != 'R' && c != 'F') c = getchar();a[i][j] = c;if(c == 'R') continue;//题目要求必须等于'F' _left[i][j] = _right[i][j] = j;_over[i][j] = 1;}}
}void work(){for(int i = 1;i <= n; ++ i)for(int j = 2;j <= m; ++ j)if(a[i][j] == a[i][j - 1] && a[i][j] == 'F')_left[i][j] = _left[i][j - 1];for(int i = 1;i <= n; ++ i)for(int j = m - 1;j >= 1; -- j)if(a[i][j] == a[i][j + 1] && a[i][j] == 'F')//注意倒序 + 1 _right[i][j] = _right[i][j + 1];for(int i = 1;i <= n; ++ i){for(int j = 1;j <= m; ++ j){if(i > 1 && a[i][j] == a[i - 1][j] && a[i][j] == 'F'){//注意边界 _left[i][j] = max(_left[i][j],_left[i - 1][j]);_right[i][j] = min(_right[i][j],_right[i - 1][j]);_over[i][j] = _over[i - 1][j] + 1;}int len1 = _right[i][j] - _left[i][j] + 1;ans = max(ans,_over[i][j] * len1);}}put(ans * 3);}int main(){//  file();readdata();work();return 0;
}

枚举所有极大子矩形

学习资料

  1. 浅谈用极大化思想解决最大子矩形问题By 王知昆
    一些说明:(引自王知昆论文:浅谈用极大化思想解决最大子矩形问题)
  1. 定义有效子矩形为内部不包含任何障碍点且边界与坐标轴平行的子矩形
  2. 极大有效子矩形:一个有效子矩形,如果不存在包含它且比它大的有效子矩形,就称这个有效子矩形为极大有效子矩形。(为了叙述方便,以下称为极大子矩形)
  3. 定义最大有效子矩形为所有有效子矩形中最大的一个(或多个)。以下简称为最大子矩形。

规定障碍数为 SSS,算法的复杂度为O(S2)O(S^2)O(S2)
则这种思想方法的核心:

算法的思路是通过枚举所有的极大子矩形找出最大子矩形
为了处理方便,首先在障碍点的集合中加上整个矩形四角上的点。
算法的思路是这样的:先枚举极大子矩形的左边界,然后从左到右依次扫描每一个障碍点,并不断修改可行的上下边界,从而枚举出所有以这个定点为左边界的极大子矩形。

还有遗漏:

可以发现,这样做只考虑到了左边界覆盖一个点的矩形,因此我们还需要枚举左边界与整个矩形的左边界重合的情况。这还可以分为两类情况。

一种是左边界与整个举行的左边界重合,而右边界覆盖了一个障碍点的情况,对于这种情况,可以用类似的方法从右到左扫描每一个点作为右边界的情况。

另一种是左右边界均与整个矩形的左右边界重合的情况,对于这类情况我们可以在预处理中完成:先将所有点按纵坐标排序,然后可以得到以相邻两个点的纵坐标为上下边界,左右边界与整个矩形的左右边界重合的矩形,显然这样的矩形也是极大子矩形,因此也需要被枚举到。

适用性:

虽然以上的算法(算法1)看起来是比较高效的,但也有使用的局限性。可以发现,这个算法的复杂度只与障碍点的个数s有关。但对于某些问题,s最大有可能达到n×m,当s较大时,这个算法就未必能满足时间上的要求了。

与悬线法比较:

以上说了两种具有一定通用性的处理算法,时间复杂度分别为O(S2)O(S^2)O(S2)和O(NM)O(NM)O(NM)。两种算法分别适用于不同的情况。从时间复杂度上来看,第一种算法对于障碍点稀疏的情况比较有效,第二种算法则与障碍点个数的多少没有直接的关系(当然,障碍点较少时可以通过对障碍点坐标的离散化来减小处理矩形的面积,不过这样比较麻烦,不如第一种算法好),适用于障碍点密集的情况。

题目

奶牛浴场

传送门:luogu1578
题目描述
由于John建造了牛场围栏,激起了奶牛的愤怒,奶牛的产奶量急剧减少。为了讨好奶牛,John决定在牛场中建造一个大型浴场。但是John的奶牛有一个奇怪的习惯,每头奶牛都必须在牛场中的一个固定的位置产奶,而奶牛显然不能在浴场中产奶,于是,John希望所建造的浴场不覆盖这些产奶点。这回,他又要求助于Clevow了。你还能帮助Clevow吗?

John的牛场和规划的浴场都是矩形。浴场要完全位于牛场之内,并且浴场的轮廓要与牛场的轮廓平行或者重合。浴场不能覆盖任何产奶点,但是产奶点可以位于浴场的轮廓上。

Clevow当然希望浴场的面积尽可能大了,所以你的任务就是帮她计算浴场的最大面积。

输入格式
输入文件的第一行包含两个整数L和W,分别表示牛场的长和宽。文件的第二行包含一个整数n,表示产奶点的数量。以下n行每行包含两个整数x和y,表示一个产奶点的坐标。所有产奶点都位于牛场内,即:0<=x<=L,0<=y<=W。

输出格式
输出文件仅一行,包含一个整数S,表示浴场的最大面积。

输入输出样例
输入 #1 复制
10 10
4
1 1
9 1
1 9
9 9
输出 #1 复制
80
说明/提示
0<=n<=5000

1<=L,W<=30000

Winter Camp 2002

感谢 @凯瑟琳98 提供了4组hack数据

分析:
你看这个边界:300003000030000,N∗MN*MN∗M的悬线法靠不住了
(谁说的?!明明可以离散化)
离散化也行,但复杂了
看障碍点数:500050005000,优秀
O(S2)O(S^2)O(S2)的算法不正好?
不过听说这个算法容易出锅,看看dalao的题解:题解 P1578 【奶牛浴场】

注意:
(1)枚举极大子矩阵方法
(2)也可以用悬线法 + 离散化
(3)输出优化写错了,竟然还有10分 ( ̄▽ ̄)
(4)down - up不是up - down
(5)还是,注意边界情况

代码:

/*************************
User:Mandy.H.Y
Language:c++
Problem:luogu1578
Algorithm:
Date: 2019.8.14
*************************/#include<bits/stdc++.h>
#define Max(x,y) ((x) > (y) ? (x) : (y))
#define Min(x,y) ((x) < (y) ? (x) : (y))using namespace std;const int maxn = 3e4 + 5;
const int maxp = 5e3 + 10;typedef long long ll;int n,m,p;
long long ans = 0;struct Node{int x,y;
}node[maxp];template<class T>inline void read(T &x){x = 0;bool flag = 0;char ch = getchar();while(!isdigit(ch)) flag |= ch == '-',ch = getchar();while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();if(flag) x = -x;
} template<class T>void putch(const T x){if(x > 9) putch(x / 10);//输出优化写错了,竟然还有10分 putchar(x % 10 | 48);
}template<class T>void put(const T x){if(x < 0) putchar('-'),putch(-x);else putch(x);
}void file(){freopen("testdata(1).in","r",stdin);
//  freopen("bath.out","w",stdout);
}void readdata(){read(n);read(m);read(p);for(int i = 1;i <= p;++ i) read(node[i].x),read(node[i].y);node[++p].x = 0;node[p].y = 0;node[++p].x = 0;node[p].y = m;node[++p].x = n;node[p].y = 0;node[++p].x = n;node[p].y = m;//防止超出矩形边界
}bool cmp1(const Node &a,const Node &b){return a.y < b.y;
}bool cmp2(const Node &a,const Node &b){return a.x < b.x;
}void work(){sort(node + 1,node + p + 1,cmp1);for(int i = 1;i <= p; ++ i){int up = 0,down = n,l = m - node[i].y;for(int j = i + 1;j <= p; ++ j){//右边在矩形边界与右边是障碍物的情况 //down - up不是up -  down if(node[j].x <= up || node[j].x >= down) continue;//在上下界之外不考虑 if((down - up) * l <= ans) break;//剪枝 ans = max(ans,(down - up) * ((long long)node[j].y - node[i].y));//作为右边界 if(node[j].x == node[i].x) break;//如果横坐标相等,后来就都是0 if(node[j].x < node[i].x) up = node[j].x;else down = node[j].x;}up = 0,down = n,l = node[i].y;for(int j = i - 1;j >= 1; -- j){//左边在矩形边界上 if(node[j].x <= up || node[j].x >= down) continue;if((down - up) * l <= ans) break;ans = max(ans,(down - up) * ((long long)node[i].y - node[j].y));if(node[j].x == node[i].x) break;if(node[j].x < node[i].x) up = node[j].x;else down = node[j].x;}}sort(node + 1,node + p + 1,cmp2);for(int i = 2;i <= p; ++ i) ans = max(ans,(long long)m * (node[i].x - node[i - 1].x));//考虑左右都在矩形边界上的情况 put(ans);
}int main(){//  file();readdata();work();return 0;
}

单调栈

学习资料

  1. 浅谈单调栈的实现方式和简单应用 By COLIN·GAO

动规 - 最大子矩阵问题相关推荐

  1. 关于数位动规(入门到进阶,难度中档)

    数位动规,就是对于数位进行动规(日常一句废话···) 刚好今天听数位dp,就总结一下最近写的题吧.郭神说要学懂数位dp,还是要搞懂它内部是怎么工作的.比如一个有大小的数,我们在这里剥夺它作为一个整数的 ...

  2. CF2B The least round way(贪心+动规)

    题目 CF2B The least round way 做法 后面\(0\)的个数,\(2\)和\(5\)是\(10\)分解质因数 则把方格中的每个数分解成\(2\)和\(5\),对\(2\)和\(5 ...

  3. 树形动规_(技能树)

    技能树(SGOI) skill.pas/c/cpp [问题描述] 玩过 Diablo 的人对技能树一定是很熟悉的.一颗技能树的每个结点都是一项技能,要学会这项技能则需 要耗费一定的技能点数.只有在学会 ...

  4. 正则表达式匹配(动规)

    文章目录 题目 思路 转移方程 特征 再探 i 和 j 代码 题目 请实现一个函数用来匹配包含 . 和 * 的正则表达式.模式中的字符 . 表示任意一个字符,而 * 表示它前面的字符可以出现任意次(含 ...

  5. 洛谷 P1073 最优贸易 (分层图状态转移+SPFA,求最长路径;另附某dalao的超短代码:暴力+动规)

    题目链接1 题目链接2 另附某dalao的超短代码:暴力+动规 P1073 最优贸易 题目描述 C国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市. 任意两个城市之间最多只有 ...

  6. leetcode 121. 买卖股票的最佳时机 (贪心 + 动规 + 双指针

    贪心的思路: 得到最小值,再挨个用数组中的值减去最小值,最终值取一个最大的 class Solution { public:int maxProfit(vector<int>& p ...

  7. 导弹拦截(动规,贪心)

    题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹 ...

  8. BTTCOJ 问题 G: 逃离地牢 树形动规

    题目描述 小明明又被大威鱼抓住了,大威鱼把小明明关在地牢里,地牢由n * n 个房间组成,小明被困在地牢的最左上角的房间中,出口在最右下角,他想逃出这个诡异的地牢,但是他只能向下或者向右走. 小明每经 ...

  9. bzoj3875 【Ahoi2014】骑士游戏 spfa处理后效性动规

    骑士游戏 [故事背景] 长期的宅男生活中,JYY又挖掘出了一款RPG游戏.在这个游戏中JYY会 扮演一个英勇的骑士,用他手中的长剑去杀死入侵村庄的怪兽. [问题描述] 在这个游戏中,JYY一共有两种攻 ...

最新文章

  1. mysql8.0取消授权_mysql8创建用户、删除用户、授权、取消授权
  2. 遏制企业数据泄露之殇,大咖切磋云安全的攻防之道
  3. 秘籍 | 机器学习数据集网址大全
  4. 【数理知识】《数值分析》李庆扬老师-第1章-数值分析与科学计算引论
  5. Nginx 反向代理、动静分离、负载均衡及配置实例
  6. oracle 计算中位数,SQL 如何计算每个分组的中位数
  7. java中各种数据类型的使用
  8. java用二维数组编写地图_[Java] Java二维数组写一个超级简单的扫雷游戏,适合新手...
  9. nginx文件下载服务器简单配置
  10. java面试要点---基础部分CoreJava,基础及语法
  11. 编写高质量JavaScript代码绳之以法(The Essentials of Writing High Quality JavaScript)翻译...
  12. python运用ico图标,处理Django中的favicon.ico图标实例方法
  13. C++11新特性,推荐使用emplace_back()替换push_back()的原因
  14. 求助!mac版cc2017安装错误
  15. C4D学习笔记3-动画-动画渲染流程案例
  16. 古籍研究社系列第6部《迟来的翅膀》读后感……吗?
  17. 智源发布《人工智能的认知神经基础白皮书》,一览“AI×脑科学”前沿
  18. 基于html篮球网页游戏,基于html5和jquery的篮球跳动游戏
  19. 377. 组合总和 Ⅳ(JavaScript,动规)
  20. Android实现QQ换头像的对话框

热门文章

  1. 又一所985大学全面改考408!厦门大学计算机考研
  2. 【Web技术】1391- 页面可视化搭建工具前生今世
  3. 计算机c盘如何腾出空间,WIN8的C盘太大怎么清理腾出空间呢
  4. 腾讯云部署hexo博客系统
  5. html的style不起作用,css样式不起作用是什么原因?
  6. 【win11】解决win11家庭版没有组策略编辑器
  7. python统计英文文章中单词出现的次数
  8. 为BlueLake主题增加自定义icon图标
  9. Cmake传入项目版本号
  10. Cesium 实现卫星效果