[DP专题]悬线法
参考:https://blog.csdn.net/twtsa/article/details/8120269
先给出题目来源:(洛谷)
......
悬线法,很好理解,就是悬一根线晃来晃去求最大子矩阵嘛!
思路和转移方程也很简单:
if(满足^&%$!@#^%){
right[i][j]=min(right[i][j],right[i-][j]);
left[i][j]=max(left[i][j],left[i-][j]);
up[i][j]=up[i-][j]+;
}
下面解释一下:
right表示从(i,j)这个点出发向右能到达最远的距离
left和up差不多,一个向左,一个向上
关于初始化
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
right[i][j]=left[i][j]=j,up[i][j]=;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(满足条件)
right[i][j]=right[i][j-];
for(int i=;i<=n;i++)
for(int j=m-;j>=;j--)
if(满足条件)
left[i][j]=left[i][j+];
其实这个东西跟模板一样套就好了
【NO.1】最大正方形
【解法1】
数据这么小,考虑暴力:
维护矩阵二维前缀和,暴力枚举左上角和正方形的长,判断该块矩阵和是否为长*长,更新最大值
复杂度:O(n^3)
(很久以前以前写的代码,可能有点丑)
#include<iostream>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,map[][];
int sum[][];
void pre(){
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
sum[i][j]=sum[i-][j]+sum[i][j-]-sum[i-][j-]+map[i][j];
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
scanf("%d",&map[i][j]);
pre();
int ans=-;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
for(int l=;l<=min(n,m);l++){
int rx=i+l-,ry=j+l-;
if(i-+l>n||j-+l>m||sum[rx][ry]-sum[rx][j-]-sum[i-][ry]+sum[i-][j-]!=l*l) break;
if(ans<l) ans=l;
}
printf("%d",ans);
return ;
}【缺点】但是如果n=5000之类稍微大一点的数据就GG了,所以接下来我们用悬线法解决这个问题
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
char chr=getchar(); int f=,ans=;
while(!isdigit(chr)) {if(chr=='-') f=-;chr=getchar();}
while(isdigit(chr)) {ans=(ans<<)+(ans<<);ans+=chr-'';chr=getchar();}
return ans*f;
}
void write(int x){
if(x<) putchar('-'),x=-x;
if(x>) write(x/);
putchar(x%+'');
}
int n,m,a[][];
int l[][],r[][],up[][],ans1,ans2;
int main(){
n=read(),m=read();
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
a[i][j]=read(),l[i][j]=r[i][j]=j,up[i][j]=;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(a[i][j]==a[i][j-]&&a[i][j]==) l[i][j]=l[i][j-];
for(int i=;i<=n;i++)
for(int j=m-;j>=;j--)
if(a[i][j]==a[i][j+]&&a[i][j]==) r[i][j]=r[i][j+];//预处理
for(int i=;i<=n;i++)
for(int j=;j<=m;j++){
if(i>)
if(a[i][j]==a[i-][j]&&a[i][j]==){//满足条件
l[i][j]=max(l[i][j],l[i-][j]);
r[i][j]=min(r[i][j],r[i-][j]);
up[i][j]=up[i-][j]+;
}
int a=r[i][j]-l[i][j]+;
int b=min(a,up[i][j]);
ans1=max(b,ans1);//更新答案
}
cout<<ans1;
return ;
}
有没有发现,其实就是模板里面把条件加上去就OK了,惊不惊喜!!!哈哈哈(其实好像下面大部分都是这样的)比如下面这题
【NO.2】棋盘制作
一样是套模板,改一下条件
注意到该题条件是10间隔分布,则if语句中内容应为:if(a[i][j]!=a[i-1][j])注意这里还有一个大前提就是i>1!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
char chr=getchar(); int f=,ans=;
while(!isdigit(chr)) {if(chr=='-') f=-;chr=getchar();}
while(isdigit(chr)) {ans=(ans<<)+(ans<<);ans+=chr-'';chr=getchar();}
return ans*f;
}
void write(int x){
if(x<) putchar('-'),x=-x;
if(x>) write(x/);
putchar(x%+'');
}
int n,m,a[][];
int l[][],r[][],up[][],ans1,ans2;
int main(){
n=read(),m=read();
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
a[i][j]=read(),l[i][j]=r[i][j]=j,up[i][j]=;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(a[i][j]!=a[i][j-]) l[i][j]=l[i][j-];
for(int i=;i<=n;i++)
for(int j=m-;j>=;j--)
if(a[i][j]!=a[i][j+]) r[i][j]=r[i][j+];
for(int i=;i<=n;i++)
for(int j=;j<=m;j++){
if(i>)
if(a[i][j]!=a[i-][j]){
l[i][j]=max(l[i][j],l[i-][j]);
r[i][j]=min(r[i][j],r[i-][j]);
up[i][j]=up[i-][j]+;
}
int a=r[i][j]-l[i][j]+;
int b=min(a,up[i][j]);
ans1=max(b*b,ans1);
ans2=max(a*up[i][j],ans2);
}
cout<<ans1<<"\n"<<ans2;
return ;
}
【NO.3】巨大的牛棚
还是模板?只不过读入的时候转换一下就好了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
char chr=getchar(); int f=,ans=;
while(!isdigit(chr)) {if(chr=='-') f=-;chr=getchar();}
while(isdigit(chr)) {ans=(ans<<)+(ans<<);ans+=chr-'';chr=getchar();}
return ans*f;
}
void write(int x){
if(x<) putchar('-'),x=-x;
if(x>) write(x/);
putchar(x%+'');
}
int n,T;
int a[][],l[][],r[][],u[][],ans;
int main(){
n=read();T=read();
for(int i=;i<=T;i++){int x=read(),y=read();a[x][y]=;}
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
l[i][j]=r[i][j]=j,u[i][j]=;
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
if(a[i][j]==&&a[i][j-]==) l[i][j]=l[i][j-];
for(int i=;i<=n;i++)
for(int j=n-;j>=;j--)
if(a[i][j]==&&a[i][j+]==) r[i][j]=r[i][j+];
for(int i=;i<=n;i++)
for(int j=;j<=n;j++){
if(i>&&a[i][j]==&&a[i-][j]==){
u[i][j]=u[i-][j]+;
l[i][j]=max(l[i-][j],l[i][j]);
r[i][j]=min(r[i-][j],r[i][j]);
}
int a=r[i][j]-l[i][j]+;
int b=min(a,u[i][j]);
ans=max(ans,b);
}
cout<<ans;
return ;
}
【NO.4】玉蟾宫
这些题几乎一样...都不想说什么了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
char chr=getchar(); int f=,ans=;
while(!isdigit(chr)) {if(chr=='-') f=-;chr=getchar();}
while(isdigit(chr)) {ans=(ans<<)+(ans<<);ans+=chr-'';chr=getchar();}
return ans*f;
}
void write(int x){
if(x<) putchar('-'),x=-x;
if(x>) write(x/);
putchar(x%+'');
}
int n,m;
char a[][];
int l[][],r[][],up[][],ans1,ans2;
int main(){
n=read(),m=read();
for(int i=;i<=n;i++)
for(int j=;j<=m;j++){
cin>>a[i][j];
l[i][j]=r[i][j]=j,up[i][j]=;
}
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(a[i][j]==a[i][j-]&&a[i][j]=='F') l[i][j]=l[i][j-];
for(int i=;i<=n;i++)
for(int j=m-;j>=;j--)
if(a[i][j]==a[i][j+]&&a[i][j]=='F') r[i][j]=r[i][j+];
for(int i=;i<=n;i++)
for(int j=;j<=m;j++){
if(i>)
if(a[i][j]==a[i-][j]&&a[i-][j]=='F'){
l[i][j]=max(l[i][j],l[i-][j]);
r[i][j]=min(r[i][j],r[i-][j]);
up[i][j]=up[i-][j]+;
}
int a=r[i][j]-l[i][j]+;
int b=min(a,up[i][j]);
ans2=max(a*up[i][j],ans2);
}
cout<<ans2*;
return ;
}
当然,难的在这里:奶牛浴场 (上面看懂的同学可以挑战一下这题)
这里有题解噢~
占坑,别禁赛噢~
[DP专题]悬线法的更多相关文章
- hdu4328(经典dp用悬线法求最大子矩形)
http://wenku.baidu.com/view/728cd5126edb6f1aff001fbb.html 关于悬线法,这里面有详解. 我当时只想到了记录最大长度,却没有想到如果连最左边和最右 ...
- BZOJ 1057: [ZJOI2007]棋盘制作( dp + 悬线法 )
对于第一问, 简单的dp. f(i, j)表示以(i, j)为左上角的最大正方形, f(i, j) = min( f(i + 1, j), f(i, j + 1), f(i + 1, j + 1)) ...
- P1169 [ZJOI2007]棋盘制作 DP悬线法
题目描述 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个8 \times 88×8大小的黑白相间的方阵,对应八八六十四卦,黑白 ...
- P4147 玉蟾宫 二维DP 悬线法
题目背景 有一天,小猫rainbow和freda来到了湘西张家界的天门山玉蟾宫,玉蟾宫宫主蓝兔盛情地款待了它们,并赐予它们一片土地. 题目描述 这片土地被分成N*M个格子,每个格子里写着'R'或者'F ...
- [ZJOI2007]棋盘制作 悬线法dp 求限制下的最大子矩阵
https://www.luogu.org/problemnew/show/P1169 第一次听说到这种dp的名称叫做悬线法,听起来好厉害 题意是求一个矩阵内的最大01交错子矩阵,开始想的是dp[20 ...
- BZOJ 1057: [ZJOI2007]棋盘制作 悬线法求最大子矩阵+dp
1057: [ZJOI2007]棋盘制作 Description 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个8*8大小的黑 ...
- 悬线法DP总结
悬线法DP总结 问题模型 求满足某种条件(如01交替)的最大矩形(正方形) 思想 先预处理出\(ml[i][j],mr[i][j],mt[i][j]\),分别表示当前位置\((i,j)\)能向左扩展到 ...
- 悬线法——有套路的DP
例题 P1169 [ZJOI2007]棋盘制作 题目描述 国际象棋是世界上最古老的博弈游戏之一,和中国的围棋.象棋以及日本的将棋同享盛名.据说国际象棋起源于易经的思想,棋盘是一个8×88 \times ...
- 【题解】洛谷P1169 [ZJOI2007] 棋盘制作(坐标DP+悬线法)
次元传送门:洛谷P1169 思路 浙江省选果然不一般 用到一个从来没有听过的算法 悬线法: 所谓悬线法 就是用一条线(长度任意)在矩阵中判断这条线能到达的最左边和最右边及这条线的长度 即可得到这个矩阵 ...
随机推荐
- ubuntu下Fiddler抓包
参考 https://www.cnblogs.com/jcli/p/4474332.html https://www.jianshu.com/p/4505c732e378 1. 你要有个Mono环境, ...
- 在Unity中对注册表的信息进行操作
问题1 在对注册表进行操作时无法生成注册表相关的类 解决办法: 增加头文件using Microsft.Win32; 问题2 在运行程序时报错同时注 ...
- mysql异地备份方案经验总结
Mysql 数据库异地备份脚本 实验环境:关闭防火墙不然不能授权登录 Mysql-server:192.168.30.25 Mysql-client: 192.168.30.24 实验要求:对mys ...
- Sersync+Rsync实现数据文件实时同步
rsync+inotify-tools与rsync+sersync架构的区别1,rsync+inotify-tools只能记录下被监听的目录发生的变化(增删改)并没有把具体变化的文件或目录记录下来在同 ...
- 洛谷——P1572 计算分数
P1572 计算分数 模拟+字符串 注意有两位数的情况以及负数情况 #include<bits/stdc++.h> using namespace std; string s; ],b[] ...
- cmake更新版本简记
问题描述: 由于需求,要在服务器上安装ANTs(Advanced Normalization Tools).然而最新版的ANTs需要下载源码并用cmake编译, 于是根据https://github. ...
- mysql命令整理
MySQL大小写通用. 一.常见用的mysql指令 1.show databases; #查看当前所有库 2.show tables; #查看所在库中的所有表 3.use 库名; #进入该库 4.sh ...
- 2019字节跳动冬令营day7娱乐赛19题题解
啊没去听讲题,也没发纸质题解,电子版题解也没有 为最后几个unsolve自闭了一段时间才全都A掉 3个队友写的我没看的题通过人数蛮多就不管了 题目地址:https://pan.baidu.com/s/ ...
- 神奇的幻方 noip2015day1 T1
题目描述 Description 幻方是一种很神奇的N∗N矩阵:它由数字 1,2,3, … … ,N∗N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个 ...
- npm install socket.io 提示缺少"VCBuild.exe"
http://www.cnblogs.com/yangzhx/p/4648501.html https://www.v2ex.com/t/120493