[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 思路 浙江省选果然不一般 用到一个从来没有听过的算法 悬线法: 所谓悬线法 就是用一条线(长度任意)在矩阵中判断这条线能到达的最左边和最右边及这条线的长度 即可得到这个矩阵 ...
随机推荐
- js的三种对象
JS中,可以将对象分为“内部对象”.“宿主对象”和“自定义对象”三种. 1,内部对象 js中的内部对象包括Array.Boolean.Date.Function.Global.Math.Number. ...
- Unity如何播放带有alpha 通道的视频
问题: 当使用Video Player播放带有alpha 通道的视频时带有黑色背景 解决方式: 使用文件格式为WEBM的视频,对视频文件进行的修改 在RawImage中,将New Render Tex ...
- servlet之@PostConstruct,@PreDestroy
1.@PostConstruct说明 被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次,类似于Serclet的inti()方法.被@PostCo ...
- 6.3.4 使用marshal 模块操作二进制文件
Python 标准库 marshal 也可以进行对象的序列化和反序列化,下面的代码进行了简单演示. import marshal x1 = 30 x2 = 5.0 x3 = [1,2,3] x4 = ...
- padding填充与box-sizing: border-box配合使用
不管伸缩盒还是浮动盒子,只要使用到padding,就必须使用 box-sizing: border-box; 有图片的时候,需摇与其他文字对齐的时候,在图片的外层加个:vertical-ali ...
- bupt summer training for 16 #8 ——字符串处理
https://vjudge.net/contest/175596#overview A.设第i次出现的位置左右端点分别为Li,Ri 初始化L0 = 0,则有ans = sum{ (L[i] - L[ ...
- 越来越好玩,SPRINGMVC
了解了JSP和SERVLET的运行机制, 看完SPRING的内容,理解了一些IOC及AOP之后,进入SPRINGMVC和SPRINGBOOT,感觉轻松多啦.
- [poj3070]Fibonacci_矩乘_快速幂
Fibonacci poj-3070 题目大意:求Fibonacci第n项. 注释:模数为10000,$1\le n \le 10^9$. 想法:矩阵题,用例题6的想法,我们构造矩阵 $\begin{ ...
- 洛谷—— P1092 虫食算
https://www.luogu.org/problem/show?pid=1092 题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简 ...
- Java排序算法之冒泡、选择、插入、快速
JavaSort Java经典排序算法代码 2018-1-26更新:冒泡排序,选择排序,插入排序,快速排序 1. 冒泡排序 特点:效率低,实现简单 思想(从小到大排): 第1个数和第2个数比较,如果第 ...