最近在写dp的题目

但是我这个同学又又又又生病了

学习了一下多叉树的背包问题和最大子矩阵的问题,还有攒了几道期望,先总结一下矩阵问题;

问题模型:

在一个给定的矩形中有一些障碍点,找出内部不包含障碍点的(或者黑白相间)、轮廓与整个矩形平行或重合的最大子矩形。

//根据问题我们改动程序,我们现在学习这种解决问题的思想;

有关论文:王知昆《浅谈用极大化思想解决最大子矩阵问题》;

那么我们先来讲一下什么是极大化思想;

先介绍一下子矩阵的概念;

有效子矩形:内部不包含障碍点的(或者满足黑白相间)、轮廓与整个矩形平行或重合的子矩形。

极大子矩形:每条边都不能向外扩展的有效子矩形。

最大子矩形:所有有效子矩形中最大的一个(或多个)。

满足:

在一个有障碍点的矩形中最大子矩形一定是极大子矩形。

枚举所有的极大子矩形,找到最大子矩形。

设NM分别为整个矩形的长和宽,S为内部的障碍点数;

我们知道极大子矩阵一定不能再向外扩展,所以极大子矩阵的边界要么为障碍点所处位置,要么与位于整个矩阵的边界;

我们可以枚举左右上下四个边界,然后判断组成的矩阵是否具有有效子矩阵;

但是枚举了很多无效的子矩阵,复杂度(S^5)

我们可以枚举左右边界,然后将边界内的点排序,每个相邻的点左右边界组成一个矩阵; 我们将复杂度降至(S^3)

但是我们枚举中一部分不是极大子矩阵;

我们要做的就是让枚举的每一个矩阵都是有效的并且是极大的;

我们可以设计以下算法:

1/将所有点按照横坐标排序,并编号1,2,3.....n;

2/枚举每一个点作为左边界,设定此时上下边界为矩阵的上下边界;

3.扫描后面的点作为右边界,确定一个极大子矩阵,根据纵坐标的关系,我们修改当前的上下边界;

4.以此类推,枚举所有的点;

但是我们还要额外考虑一些情况:

1.矩形的左边界和矩阵的左边界重合;

这依旧可以分类讨论(1),左边界与矩阵左边界重合,而右边界覆盖了一个障碍点,我们可以从右向左是扫描,把当前点作为右边界的情况;

(2)左右边界都与矩阵的左右边界重合,我们可以预处理出来,具体做法就是按照纵坐标从小到达排序,相邻两个点纵坐标上下边界为上下边界,而矩阵左右边界作为边界,

这也是我们需要枚举到的;

但是这个题适用于障碍点较少的矩阵中,代码容易;时间复杂度(NM) 空间复杂度(NM)

除了这种方法,对于障碍点比较密集的情况,我们可以采取悬线法解决;

我们定义 :

有效竖线:对于上下两个端点不包含障碍点的竖直直线;

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

每个悬线都与它底部的点一一对应,矩形中的每一个点(矩形顶部的点除外)都对应了一个悬线。

悬线的个数=(N-1)*M;

对于一个极大子矩阵按照横坐标不同我们切割多个与y轴平行的线段,那么其中至少有一个悬线;

我们将一个悬线对应向左右拓展,就得到一个矩阵,悬线对应的矩阵不一定是极大矩阵,下坐标可能拓展;

所以极大矩阵个数<=悬先个数;

我们枚举每一个悬线,找到极大子矩阵;

我们设left[i][j]为(i,j)对应的悬线做多能向左拓展的位置;

同样的,right[i][j]为(i,j)对应的悬线做多能向右拓展的位置;

up[i][j]为]为(i,j)对应的悬线的长度;

我们考虑(i,j)和(i-1,j)的对应关系;

如果(i-1,j)是障碍点,那么(i,j)对应悬线长度为1,左右边界为整个矩阵的左右边界;

如果(i-1,j)步是障碍点,那么(i,j)对应的悬线长度为up[i][j]=up[i-1][j]+1,对应我们改变left[i][j]为max(left[i][j],left[i-1][j]),right=min(right[i][j],right[i-1][j]);

注意初始化;以下代码是寻找全为0的最大矩阵,1为障碍点;

    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) l[i][j]=r[i][j]=j;up[i][j]=1;for(int i=1;i<=n;i++)for(int j=2;j<=n;j++)if(a[i][j]==0&&a[i][j-1]==0) l[i][j]=l[i][j-1];for(int i=1;i<=n;i++)for(int j=n-1;j>0;j--)if(a[i][j]==0&&a[i][j+1]==0)r[i][j]=r[i][j+1];    

实现起来比较容易,我们接下来看几道例题;

luogu1578奶牛浴室

这里用到第一种极大化思想,按照前面所讲的排序,我中间对于j特判了==n和==1的情况,没有的话会wa一个点,也就是极大化矩阵是左右边界的情况,其实后来有的题解并没有这一点

有待解决这个问题,而且洛谷第一篇题解我测了以下一个数据,被hack掉了;

题目第是11组数据:

6 4
4
1 2
4 1
4 3
2 1

输出应该是10

但是第一篇题解是9;

我也是频频这里出错;

#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x) {x=0;T f=1,ch=getchar();while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}x*=f;
}
int L,H,n;
struct node {int x,y;
} a[5010];
inline bool cmp1(node A,node B) {if(A.x!=B.x) {return A.x<B.x;}else {return A.y<B.y;}
}
inline bool cmp2(node A,node B) {if(A.y!=B.y) {return A.y<B.y;} else {return A.x<B.x;}
}
int main() {read(L); read(H); read(n);for(int i=1;i<=n;i++) {read(a[i].x); read(a[i].y);}a[++n].x=0,a[n].y=0;a[++n].x=0,a[n].y=H;a[++n].x=L,a[n].y=0;a[++n].x=L,a[n].y=H;sort(a+1,a+1+n,cmp1);int ans=0;for(int i=1;i<=n;i++) {int l=0,h=H,maxl=L-a[i].x;for(int j=i+1;j<=n;j++) {if(j==n) {//这个特判不写不过第11组?? ans=max(ans,maxl*(h-l));} else {if(a[j].y<=h&&a[j].y>=l) {if(maxl*(h-l)<=ans) {break;}ans=max(ans,(a[j].x-a[i].x)*(h-l));if(a[j].y==a[i].y) {break;}if(a[j].y>a[i].y) {h=min(h,a[j].y);} else {l=max(l,a[j].y);}}}}l=0,h=H,maxl=a[i].x;for(int j=i-1; j>=1; j--) {if(a[j].y<=h&&a[j].y>=l) {if(maxl*(h-l)<=ans) {break;}ans=max(ans,(a[i].x-a[j].x)*(h-l));if(a[j].y==a[i].y) {break;}if(a[j].y>a[i].y) {h=min(h,a[j].y);} else {l=max(l,a[j].y);}}if(j==1) {ans=max(ans,maxl*(h-l));}}}sort(a+1,a+1+n,cmp2);for(int i=1;i<n;i++) {ans=max(ans,(a[i+1].y-a[i].y)*L);}printf("%d",ans);return 0;
}

View Code

棋盘制作

给定矩阵,寻找一个黑白相间最大矩形和最大正方形;

那么我们只需要特判不等于就可以了,这里利用了悬线法;

代码也是比较方便好写,我个人比较喜欢这一种;

#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x) {x=0;T f=1,ch=getchar();while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();x*=f;
}
const int N=2010;
int n,m;
int a[N][N],l[N][N],r[N][N],up[N][N],ans1,ans2;
int main() {read(n); read(m);for(int i=1;i<=n;i++) {for(int j=1;j<=m;j++) {read(a[i][j]);l[i][j]=r[i][j]=j;up[i][j]=1;}}for(int i=1;i<=n;i++) {for(int j=2;j<=m;j++) {if(a[i][j]!=a[i][j-1]) {l[i][j]=l[i][j-1];}}}for(int i=1;i<=n;i++) {for(int j=m-1;j>0;j--) {if(a[i][j]!=a[i][j+1]) {r[i][j]=r[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]) {l[i][j]=max(l[i][j],l[i-1][j]);r[i][j]=min(r[i][j],r[i-1][j]);up[i][j]=up[i-1][j]+1;}int a=r[i][j]-l[i][j]+1;int b=min(a,up[i][j]);ans1=max(ans1,b*b);ans2=max(ans2,a*up[i][j]);}}cout<<ans1<<endl<<ans2<<endl;return 0;
}

View Code

玉蟾宫

寻找最大全0矩形,裸题,和上题目一样;按照题目要求答案*3;

#include<bits/stdc++.h>
using namespace std;
int n,m,a[1010][1010],l[1010][1010],r[1010][1010],up[1010][1010];
char ch[1010][1010];
template<typename T>inline void read(T &x) {x=0;T f=1,ch=getchar();while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}x*=f;
}
int main() {//freopen("1.in","r",stdin);
    read(n); read(m);for(int i=1;i<=n;i++) {for(int j=1;j<=m;j++) {cin>>ch[i][j];if(ch[i][j]=='F') a[i][j]=0;else a[i][j]=1;l[i][j]=r[i][j]=j;up[i][j]=1;}}for(int i=1;i<=n;i++)for(int j=2;j<=m;j++)if(a[i][j]==0&&a[i][j-1]==0) l[i][j]=l[i][j-1];for(int i=1;i<=n;i++)for(int j=m-1;j>0;j--)if(a[i][j]==0&&a[i][j+1]==0)r[i][j]=r[i][j+1];int ans=0;for(int i=1;i<=n;i++) {for(int j=1;j<=m;j++){if(i>1&&a[i][j]==0&&a[i-1][j]==0){r[i][j]=min(r[i][j],r[i-1][j]);l[i][j]=max(l[i][j],l[i-1][j]);up[i][j]=up[i-1][j]+1;}ans=max(ans,(r[i][j]-l[i][j]+1)*up[i][j]);}}cout<<ans*3<<endl;
} 

View Code

巨大的牛棚

裸题,寻找没有障碍点,这大概好几倍经验了;是正方形;

#include<bits/stdc++.h>
using namespace std;
int n,m,T,x,y,a[1010][1010],l[1010][1010],r[1010][1010],up[1010][1010];
template<typename T>inline void read(T &x) {x=0;T f=1,ch=getchar();while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}x*=f;
}
int main() {//freopen("1.in","r",stdin);
    read(n);read(T);memset(a,0,sizeof(a));for(int i=1;i<=T;i++) {read(x); read(y);a[x][y]=1;}for(int i=1;i<=n;i++) {for(int j=1;j<=n;j++) {l[i][j]=r[i][j]=j;up[i][j]=1;}}for(int i=1;i<=n;i++)for(int j=2;j<=n;j++)if(a[i][j]==0&&a[i][j-1]==0) l[i][j]=l[i][j-1];for(int i=1;i<=n;i++)for(int j=n-1;j>0;j--)if(a[i][j]==0&&a[i][j+1]==0)r[i][j]=r[i][j+1];int ans=0;for(int i=1;i<=n;i++) {for(int j=1;j<=n;j++) {if(i>1&&a[i][j]==0&&a[i-1][j]==0){r[i][j]=min(r[i][j],r[i-1][j]);l[i][j]=max(l[i][j],l[i-1][j]);up[i][j]=up[i-1][j]+1;}int a=r[i][j]-l[i][j]+1;int b=min(a,up[i][j]);ans=max(ans,b);}}cout<<ans<<endl;return 0;
} 

View Code

最大正方形

和棋盘制作一样,再加一倍经验;

#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x) {x=0;T f=1,ch=getchar();while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();x*=f;
}
const int N=2010;
int n,m;
int a[N][N],l[N][N],r[N][N],up[N][N],ans1,ans2;
int main() {read(n); read(m);for(int i=1;i<=n;i++) {for(int j=1;j<=m;j++) {read(a[i][j]);l[i][j]=r[i][j]=j;up[i][j]=1;}}for(int i=1;i<=n;i++) {for(int j=2;j<=m;j++) {if(a[i][j]!=a[i][j-1]) {l[i][j]=l[i][j-1];}}}for(int i=1;i<=n;i++) {for(int j=m-1;j>0;j--) {if(a[i][j]!=a[i][j+1]) {r[i][j]=r[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]) {l[i][j]=max(l[i][j],l[i-1][j]);r[i][j]=min(r[i][j],r[i-1][j]);up[i][j]=up[i-1][j]+1;}int a=r[i][j]-l[i][j]+1;int b=min(a,up[i][j]);ans1=max(ans1,b);//ans2=max(ans2,a*up[i][j]);
        }}cout<<ans1; return 0;
}

View Code

我认为是很好理解的,代码也很好写;

转载于:https://www.cnblogs.com/Tyouchie/p/11382288.html

最大子矩阵问题悬线法 学习小结相关推荐

  1. 最大子矩阵问题悬线法 学习笔记

    学习材料:王知昆<浅谈用极大化思想解决最大子矩阵问题> [最大子矩阵问题] 在一个给定的矩形中有一些障碍点,找出内部不包含障碍点的.轮廓与整个矩形平行或重合的最大子矩形. [定义子矩形] ...

  2. 算法学习笔记(2) 悬线法

    本文属于「算法学习」系列文章之一.之前的[数据结构和算法设计]系列着重于基础的数据结构和算法设计课程的学习,与之不同的是,这一系列主要用来记录对大学课程范围之外的高级算法学习.优化与使用的过程,同时也 ...

  3. [ZJOI2007]棋盘制作 悬线法dp 求限制下的最大子矩阵

    https://www.luogu.org/problemnew/show/P1169 第一次听说到这种dp的名称叫做悬线法,听起来好厉害 题意是求一个矩阵内的最大01交错子矩阵,开始想的是dp[20 ...

  4. 【BZOJ-30391057】玉蟾宫棋盘制作 悬线法

    3039: 玉蟾宫 Time Limit: 2 Sec  Memory Limit: 128 MB Submit: 753  Solved: 444 [Submit][Status][Discuss] ...

  5. 【BZOJ-1127】KUP 悬线法 + 贪心

    1127: [POI2008]KUP Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special Judge Submit: 317  Solved: 1 ...

  6. BZOJ 3039: 玉蟾宫( 悬线法 )

    最大子矩阵...悬线法..时间复杂度O(nm) 悬线法就是记录一个H向上延伸的最大长度(悬线), L, R向左向右延伸的最大长度, 然后通过递推来得到. ----------------------- ...

  7. [ZJOI2007] 棋盘制作(单调栈 / DP悬线法)

    problem 洛谷链接 solution1-单调栈 很容易想到,预处理出每个点向上最大能延伸的长度,然后对每个点求一个矩阵面积. 然后思考优化,不难想到每次对一行进行求解. 每一行的所有列一起构成了 ...

  8. 2018.09.29 bzoj3885: Cow Rectangles(悬线法+二分)

    传送门 对于第一个问题,直接用悬线法求出最大的子矩阵面积,然后对于每一个能得到最大面积的矩阵,我们用二分法去掉四周的空白部分来更新第二个答案. 代码: #include<bits/stdc++. ...

  9. BZOJ1127 POI2008KUP(悬线法)

    首先显然地,如果某个格子的权值超过2k,其一定不在答案之中:如果在[k,2k]中,其自身就可以作为答案.那么现在我们只需要考虑所选权值都小于k的情况. 可以发现一个结论:若存在一个权值都小于k的矩阵其 ...

最新文章

  1. 计算机维修工国家职业标准,计算机维修工国家职业标准.pdf
  2. Seata RPC 模块的重构之路
  3. [vue] 怎么使css样式只在当前组件中生效?
  4. leetcode283. 移动零 比官方更好的解法。
  5. js中if表达式判断规则
  6. 路由器的工作原理_VRRP(虚拟路由器冗余协议)知识点梳理
  7. Impala介绍优缺点
  8. python split拆分字符串_python实现字符串完美拆分split()的方法
  9. 调用赋码远程服务异常_Remoting远程访问的这个异常怎么处理???
  10. LabVIEW编程LabVIEW开发Keithley 6485例程与相关资料
  11. 《Automation in Construction》期刊介绍(SCI 2区)
  12. Tomat连接MySQL示例
  13. NVIDIA 3D VISION 在戴尔Alienware/XPS系列上的使用
  14. puzzle(0631)纯逻辑推理
  15. Unity3d导出的安卓包如何在真机上看log打印
  16. 怎么将视频进行压缩?视频压缩简单的步骤是什么?
  17. ABAP SY标签一览表
  18. 欧美是怎么做创新的?
  19. Istio-PilotDiscovery服务的启动
  20. 由阮一峰的博客想到的

热门文章

  1. 云南大学软件学院计算机网络实验三,云南大学 软件学院 计网实验5
  2. 云服务器 文件服务,云服务 做文件服务器
  3. transformers task_summary
  4. pytorch torch.linspace
  5. `object.__init__`
  6. javascript Element对象
  7. MySQL Create JSON Values
  8. 使用keras理解LSTM
  9. Pandas 分割字符串
  10. mysql表空间转移_mysql共享表空间扩容,收缩,迁移